Activity作为Android的四大组件之首,生命周期当然是重中之重了。
虽然都是老生常谈的面试题了。。。但是被面试官问到了,还是不会的话,
尴尬不是一点点啊。(⊙﹏⊙)b
Activity—其实我更愿意喊它一声“界面”呗。我们在手机上看到的一个窗口,就是它啊。
它的生命周期也分为两种情况:
第一:正常情况下的生命周期
第二:非正常情况下的生命周期:比如屏幕翻转或者内存不足,被kill掉了。
一、 Activity有哪些生命周期方法
先来过滤一下,一共有哪些生命周期的方法吧!
onCreate:表示窗口正在被创建,比如加载layout布局文件啊(setContentView)。 所以我们可以在这个方法中,做一些初始化的操作。
onStart:表示Activity正在被启动,即将开始,此时的窗口已经可见了,但是还没有出现在前台,所以无法和用户进行交互。也就是说此时的窗口正处在 不可见—>可见 的过程中。
onRestart:表示窗口正在重新启动。在什么场景下会调用这个呢?比如:从A页面进入B页面,然后点击BACK键(或者自己的返回上一页的按钮)回到A页面,那么就会调用A页面的onRestart方法了。。(当前也牵扯到A和B页面的其他生命周期方法的调用,这个我们后面再详细说明)
再比如:点击HOME键回到桌面,然后通过点击任务栏或者点击应用图标再次进入A页面,都可以触发调用这个方法
onResume:表示此时的窗口已经可见了,显示在前台并且进行活动了,我们也可以与窗口进行交互了。
onPause:表示窗口正在停止,这是我们可以做一些存储数据、或者停止动画等一些不太耗时的操作,因为会影响到下一个Activity的显示。onPause执行完成之后,新的Activity的onResume才会执行。
onStop:表示窗口即将停止,此时,可以做一些稍微重量级的回收工作,但是也不能太耗时哈。
onDestroy:表示窗口即将被销毁。这是Activity生命周期中的最后一步了。这里,我们可以做一些回收工作和最终的资源释放工作。
下面,我们暴露一张图,来详细的描述一下窗口的生命周期的切换过程:
(此图是任玉刚的《Android开发艺术探索》里面的,懒,不想自己画了。)
二、正常情况下,Activity的生命周期调用过程
然后我们来梳理一下几种情况下,生命周期的方法调用顺序是怎么样的。
启动一个特定的Activity时,第一次启动,生命周期回调如下:
onCreate –> onStart –> onResume
当用户打开新的Activity或者切换到桌面的时候,回调如下:
onPause –> onStop
但是有一种特殊的情况,如果新的Activity采用了透明的主题,那么当前Activity不会回调onStop
当用户再次回到原Activity时,回调如下:
onRestart –> onStart –> onResume
当用户点击Back键回退时,回调:
onPause –> onStop –> onDestory
其中有两个问题:
第一:onStart和onResume,onPause和onStop差不对,但是又有什么本质的不同呢?
第二:当从A 窗口打开B窗口时,B窗口的onResume和A的onPause方法哪个先执行呢?
首先第一个问题:
从上面的描述中就知道了,虽然差不多,但是角度还是不通的。onStart和onStop从Activity是否可见来说的,onResume和onPause从Activity是否在前台来说的。其他的就没有什么区别了。
第二问题:
自己实践一下就知道了,先是A的onPause方法调用结束之后,才会执行B窗口的onResume方法。当然还是从源码了解的更彻底。。。请参考任玉刚的《Android开发艺术探索》或者自己去 AndroidXRef 看吧。
三、非正常情况下,Activity的生命周期调用过程
非正常情况下,比如屏幕的旋转,或者内存不够用的情况下,由系统的回收操作造成的非前台的Activity被意外杀死的情况。
盗一图,来源:任玉刚的《Android开发艺术探索》
屏幕旋转下的生命周期
我们用实例来说明
在LunchAActivity中,我们在Logcat中打印所有的生命周期方法
import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Button;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import demo.learn.com.learndemo.R;
import demo.learn.com.learndemo.utls.LogUtils;
public class LunchAActivity extends AppCompatActivity {
@BindView(R.id.btn_jump)
Button btnJump;
@BindView(R.id.tv_save)
TextView tvSave;
private String saveValues = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lunch_a);
ButterKnife.bind(this);
LogUtils.logE("LunchAActivity", "onCreate");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("savevalue", "意外保存的结果");
LogUtils.logE("LunchAActivity", "onSaveInstanceState");
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
saveValues = savedInstanceState.getString("savevalue");
LogUtils.logE("LunchAActivity", "onRestoreInstanceState");
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onRestoreInstanceState(savedInstanceState, persistentState);
}
@Override
protected void onRestart() {
super.onRestart();
LogUtils.logE("LunchAActivity", "onRestart");
}
@Override
protected void onStart() {
super.onStart();
LogUtils.logE("LunchAActivity", "onStart");
}
@Override
protected void onResume() {
super.onResume();
LogUtils.logE("LunchAActivity", "onResume");
tvSave.setText(saveValues);
}
@Override
protected void onPause() {
super.onPause();
LogUtils.logE("LunchAActivity", "onPause");
}
@Override
protected void onDestroy() {
super.onDestroy();
LogUtils.logE("LunchAActivity", "onDestroy");
}
@OnClick(R.id.btn_jump)
public void onViewClicked() {
Intent intent = new Intent(this, LunchBActivity.class);
startActivity(intent);
}
}
然后,旋转屏幕之后,打印出来的生命周期如下:
即在onDestory之前先调用onSaveInstanceState保存一些恢复页面时需要的数据,然后重新走onCreate–>onStart方法,在onResume之前调用onRestoreInstanceState获取存储的数据,然后可以在onResume方法中,将获取的数据赋值给控件等等操作。
注意这里不走onRestart方法,这里是销毁窗口之后并重建,慢慢领会一下,实在不行,自己动手操作实践一遍就哦了。
资源内存不足导致低优先级的Activity被杀死的情况下
这种情况不好模拟,但是保存数据的过程和恢复数据的过程是一模一样的。
而且回收过程中,牵扯到Activity的优先级,所以我们来讨论一下Activity的优先级:
前台Activity
正在和用户进行交互的窗口,优先级最高
可见但是非前台的Activity
比如Activity中弹出一个对话框,导致Activity可见但是不可交互
后台Activity
已经被暂停的Activity,比如执行了onStop方法的窗口,优先级最低
当系统内存不足时,就会按照优先级去kill掉目标Activity所在的进程,并执行onSaveInstanceState和onRestoreInstanceState来保存和恢复数据。
而且一个进程中如果没有四大组件在运行,那么这个进程很容易被杀死。
所以一些后台进程不适合脱离四大组件独立运行。最好将一些重要的后台操作放在Service中,这样保证一定的优先级,就不会轻易被系统杀死。
如果我们想让屏幕在旋转时不销毁并重建页面,可以在AndroidManifest.xml中给Activity配置一下configChanges,如下图
两个参数缺一不可
并且在Activity中重新onConfigurationChanged方法
这样在旋转屏幕时,就不会销毁并重建Activity了。只会调用这个onConfigurationChanged回调方法。
当然我们也可以禁止屏幕旋转,通过 android:screenOrientation=”portrait”只让屏幕保持竖屏。