Activity作为Android的四大组件之首,生命周期当然是重中之重了。
虽然都是老生常谈的面试题了。。。但是被面试官问到了,还是不会的话,
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时,第一次启动,生命周期回调如下:
当用户打开新的Activity或者切换到桌面的时候,回调如下:
但是有一种特殊的情况,如果新的Activity采用了透明的主题,那么当前Activity不会回调onStop当用户再次回到原Activity时,回调如下:
当用户点击Back键回退时,回调:
- 在生命周期中,onCreate和onDestroy是对应的,创建和销毁,并且在生命周期过程中,只被调用一次。从Activity是否可见来说,onStart和onStop是配对的,这两个方法有可能会被多次调用。从Activity是否在前台来说,onResume和onPause是配对的,也有可能会被多次调用。
其中有两个问题:
第一: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); } }
正常打开页面的生命周期回调如下:
然后,旋转屏幕之后,打印出来的生命周期如下:
注意这里不走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”只让屏幕保持竖屏。
待定……
下一章节,复习一下Activity的四中启动模式。。。