Android开发经验谈Android开发Android技术知识

Activity生命周期与启动模式

2018-10-17  本文已影响18人  四会歌神陈子豪

目录

一、生命周期

二、启动模式

三、总结


一、生命周期

1. Activity的各种生命周期

系统中的Activity由Activity堆栈管理,当启动一个新的Activity的时候,这个Activity被放置在栈顶,并处于正在运行状态。前一个Activity在堆栈中位于新的Activity下面,并且在新的Activity退出前不会出现在前台。

下面用一张图展示Activity完整生命周期:

图来自于Android官网

完整生命周期:
完整生命周期在Activity第一次创建调用onCreate方法到销毁前调用onDestroy方法之间。
可视生命周期:
可视生命周期在Activity调用onStart方法变为可视到调用onStop方法变为不可视之间。
前台生命周期:
前台生命周期在Activity调用onResume方法变得可与用户交互到调用onPause方法变得不可与用户交互之间。

2. onSaveInstanceState() 与 onRestoreInstanceState()

系统在“未经你许可”销毁Activity时调用onSaveInstanceState方法,用于保存Activity状态信息。onRestoreInstanceState方法在Activity被系统销毁之后恢复Activity时被调用,用于恢复Activity状态信息。可重写这两个方法在系统“未经你许可”销毁Activity时保存和恢复你的数据。
所谓“未经你许可”指的是非人为销毁Activity,例如按下回退键退出页面属于人为,系统因内存不足回收销毁Activity属于非人为。

3. Activity生命周期的变化

分别新建3个Activity,MainActivity、TestActivity、TransparentActivity,并在每个Activity中重写onCreate,onStart,onResume,onPause,onStop,onDestroy,onRestart,onSaveInstanceState,onRestoreInstanceState这9个方法,如下所示:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.act_main)
        Log.e("MainActivity", "onCreate")
    }

    override fun onStart() {
        super.onStart()
        Log.e("MainActivity", "onStart")
    }

    override fun onResume() {
        super.onResume()
        Log.e("MainActivity", "onResume")
    }

    override fun onPause() {
        super.onPause()
        Log.e("MainActivity", "onPause")
    }

    override fun onStop() {
        super.onStop()
        Log.e("MainActivity", "onStop")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e("MainActivity", "onDestroy")
    }

    override fun onRestart() {
        super.onRestart()
        Log.e("MainActivity", "onRestart")
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
        super.onRestoreInstanceState(savedInstanceState)
        Log.e("MainActivity", "onRestoreInstanceState")
    }

    override fun onSaveInstanceState(outState: Bundle?) {
        super.onSaveInstanceState(outState)
        Log.e("MainActivity", "onSaveInstanceState")
    }
}

代码准备好了,来观察下面各种请看下Activity生命周期的变化:

启动Activity,首先创建Activity,然后页面变得可见,最后页面准备好与用户交互,所以生命周期的调用顺序应该是 onCreate() -> onStart() -> onResume(),如下图所示: 点击回退键退出Activity,首先停止与用户交互,然后页面变得不可见,最后销毁Activity,所以生命周期的调用顺序应该是 onPause() -> onStop() -> onDestory(),如下图所示: 按下HOME键后,首先页面停止所有交互,然后页面变得不可见,最后回到桌面。这时候页面并没有被销毁,只是转入后台,但是也存在被系统回收的风险,所以系统会在onPause和onStop之间调用onSaveInstanceState方法保存Activity状态信息,所以生命周期的调用顺序应该是 onPause() -> onSaveInstanceState() -> onStop(),如下图所示: 再次点击应用,如果应用没有被系统回收,只是从后台调回前台,那么首先是重新打开页面,然后页面变得可见,最后页面变得可以与用户交互,所以生命周期的调用顺序应该是 onRestart() -> onStart() -> onResume(),如下图所示: 当我们切换到其他应用的时候,页面首先是停止与用户交互,然后页面变得不可见,然后进入到别的应用中,由于页面存在被系统回收的风险,所以系统会在onPause和onStop之间调用onSaveInstanceState方法保存Activity状态信息,所以生命周期的调用顺序应该是 onPause() -> onSaveInstanceState() -> onStop(),如下图所示: 切回来的时候,如果应用没有被系统回收,首先是重新打开页面,然后页面变得可见,最后页面变得可以与用户交互,所以生命周期的调用顺序应该是 onRestart() -> onStart() -> onResume(),如下图所示: 锁屏的时候,页面首先是停止与用户交互,然后页面变得不可见,然后锁屏,由于页面存在被系统回收的风险,所以系统会在onPause和onStop之间调用onSaveInstanceState方法保存Activity状态信息,所以生命周期的调用顺序应该是 onPause() -> onSaveInstanceState() -> onStop(),如下图所示: 解锁的时候,如果应用没有被系统回收,首先是重新打开页面,然后页面变得可见,最后页面变得可以与用户交互,所以生命周期的调用顺序应该是 onRestart() -> onStart() -> onResume(),如下图所示: 从MainActivity跳转到TestActivity,首先停止MainActivity的交互,然后创建TestActivity,然后TestActivity变得可见,然后TestActivity变得可以与用户交互,最后MainActivity变得不可见,由于MainActivity存在被系统回收的风险(可能性不大),所以系统会调用onSaveInstanceState方法保存MainActivity状态信息。生命周期的调用顺序如下图所示: 点击回退键返回MainActivity,首先停止TestActivity的交互,然后重新打开MainActivity,然后MainActivity变得可见,然后MainActivity变得可以与用户交互,然后TestActivity变得不可见,最后TestActivity被销毁。生命周期的调用顺序如下图所示:

在styles中创建一个透明的主题:

<style name="AppTransparentTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="windowNoTitle">true</item>
        <item name="android:colorBackgroundCacheHint">@null</item>
        <item name="android:windowIsTranslucent">true</item>
</style>

TransparentActivity在AndroidManifest中声明透明主题

 <activity android:name=".TransparentActivity"
            android:theme="@style/AppTransparentTheme"/>
从MainActivity跳转到透明的Activity,也就是TransparentActivity。由于TransparentActivity是一个透明的Activity,所以他的底层MainActivity还是可视的,所以MainActivity的onStop方法没有被调用,只调用了onPause方法。生命周期的调用如下图所示: 点击返回键,回到MainActivity。因为MainActivity跳转到TransparentActivity时并没有变为不可视,所以没有调用onStop,回到MainActivity之后,也不需要重新启动MainActivity,所以只需要恢复交互就可以了。生命周期的调用如下图所示: 旋转屏幕后,系统会销毁然后启动该Activity,首先停止Activity的交互,然后Activity变得不可见,然后销毁Activity,销毁过后启动Activity,然后Activity变得可见且屏幕旋转,最后旋转屏幕过后的Activity变得可以交互。由于旋转屏幕属于“未经你许可”销毁Activity,所以同步执行onSaveInstanceState和onRestoreInstanceState这两个方法来保存和恢复Activity的状态。生命周期的调用顺序如下图所示:

二、启动模式

Activity储存在Activity栈中,每次创建Activity都会在Activity栈中添加,Activity被销毁时也会从Activity栈中退出。大量的创建、销毁Activity会对系统造成很大开销,为了节省开销,我们可能会复用一些Activity,Activity的启动模式就是为了方便复用Activity而设计的。下面来看一下Activity的4种启动模式:

1. 标准模式 standard

standard是Activity的默认启动模式,如果没有指定启动模式的话,这个Activity就是标准模式。我们可以在AndroidManifast里面为Activity设置启动模式,如下所示:

<activity
    android:name=".MainActivity"
    android:launchMode="standard" />
启动模式为标准模式的Activity,每次启动的时候都会在Activity栈顶创建一个实例,举个例子: 创建两个Activity,并在各个声明周期中打一个log。当前为MainActivity,点击下面的按钮跳转到启动模式为标准模式的AActivity,这时Activity栈里面有两个Activity,栈顶为AActivity。我们可以通过adb命令来查看当前应用中Activity栈里的信息,在AndroidStudio的Terminal中输入命令:adb shell " dumpsys activity | grep xxx.xxx.xxx.xxx",xxx为你的包名。按下回车后会输出一大段信息,可以从中找到一段这样的:

图中StackId为Activity的Id,sz为栈内Activity的数量,AActivity在栈内的位置位于MainActivity的上方。

上面是MainActivity跳转到AActivity的例子,那么如果多次启动启动模式为标准模式的MainActivity,如下所示: Activity栈内有两个MainActivity,再来看一下生命周期:

从上图可看到MainActivity被前后创建了两次。既然都是MainActivity,我们可以用其他启动模式来复用MainActivity,减少系统开销。下面来看一下其他启动模式。

2. 栈顶复用模式 singleTop

栈顶复用模式指的是,假如Activity处于栈顶,再次启动这个Activity的时候,复用该Activity。举个例子:
把AActivity的启动模式设置为栈顶复用模式

<activity
    android:name=".AActivity"
    android:launchMode="singleTop" />

现在从MainActivity跳转到AActivity,再从AActivity创建自己,执行adb命令查看Activity栈,如下所示:

先看一下生命周期。

从MainActivity跳转到AActivity时调用了了onCreate方法,AActivity创建自己时,只调用了onPause方法,然后马上调用onResume方法,说明AActivity在栈顶时,通过栈顶复用模式再次启动自己的时候得到了复用。

再来看一下Activity栈,只有一个AActivity:

启动模式为栈顶复用模式的AActivity在栈顶时,创建自己能得到复用,那么不在栈顶的时候呢?下面来看一下:
新建一个BActivity,现在执行的动作为从MainActivity跳转到AActivity,然后从AActivity跳转到BActivity,然后再从BActivity跳转到AActivity,查看AActivity是否复用。

先看生命周期: AActivity分别在从MainActivity跳转和从BActivity跳转的时候都调用了onCreate方法,说明创建了两个AActivity。因为从AActivity跳转到BActivity的时候,BActivity在栈顶,所以从BActivity跳转到AActivity,的时候,不能复用。再来看看Activity栈:

一共4个Activity,其中有两个AActivity,没有被复用。这种情况下,如果想复用AActivity,可以使用栈内复用模式。下面来看看栈内复用模式。

3. 栈内复用模式 singleTask

栈内复用模式跟栈顶复用模式相似,只要在同一个栈内启动模式为栈内复用模式的Activity,再次启动的时候就可以复用。先举一个跟栈顶复用模式相似的例子:
把AActivity的启动模式设置为栈顶复用模式

<activity
    android:name=".AActivity"
    android:launchMode="singleTop" />
现在从MainActivity跳转到AActivity,再从AActivity创建自己,执行adb命令查看Activity栈,如下所示: 从结果来看跟栈顶复用模式一样,AActivity得到了复用。下面再从AActivity跳转到BActivity,然后从BActivity跳转到AActivity,执行adb命令查看Activity栈: 结果也是只有两个Activity,AActivity在栈顶。BActivity去哪了呢?来看一下生命周期:

上图中AActivity只调用了1次onCreate,说明AActivity得到了复用,BActivity在最后执行了onDestroy方法,说明BActivity被销毁了。原来栈内复用模式还有清除顶部的效果,当AActivity跳转到BActivity时,BActivity在栈顶,这个时候BActivity跳转到栈内复用模式的AActivity,AActivity来到了栈顶,之前在AActivity上面的全部被顶出栈外,也就是被清除了。

4.单实例模式 singleInstance

被设置启动模式为单实例模式的Activity独自享有一个Activity栈。举个例子:
把AActivity启动模式设置为单实例模式:

<activity
    android:name=".AActivity"
    android:launchMode="singleInstance" />
MainActivity跳转到AActivity,然后从AActivity跳转到BActivity,查看生命周期和Activity栈的信息: 上图中,#41和#42是两个不同的Activity,由于AActivity启动模式是单实例模式,独享一个栈,而BActivity不是单实例模式,属于默认栈,这时栈顶为BActivity,点击返回键,退回到MainActivity,如下所示: 再看一下生命周期: 由于栈顶是BActivity,与MainActivity是同一个栈,所以在BActivity出栈被销毁后,MainActivity回到栈顶,而AActivity在另一个栈中,不受影响。这个时候再次跳转到AActivity,AActivity得到复用,生命周期如下所示:

总的来说,单实例模式的Activity就是一旦被创建,除非被销毁,否则无论从哪里跳转该Activity,都能得到复用。

5. Intent中设置启动模式

除了在AndroidManifest里设置Activity的启动模式之外,还能在startActivity时传入的Intent里设置启动模式,下面列举一些常用的启动模式标志:


三、总结

Activity是Android四大组件之一,也是与用户交互最频繁的组件。本篇文章主要介绍了Activity的生命周期与启动模式。掌握这两个知识点能让我们在开发时得到很大的便利,以最正确的姿势写出想要的效果。

微信扫描下方二维码关注微信公众号"AndroidCzh"一起学习吧!这里将长期为您分享原创文章、Android开发经验等!


另外还有Android开发QQ交流群: 705929135
上一篇下一篇

猜你喜欢

热点阅读