探索Activity的生命周期和启动模式(Kotlin)

Activity作为Android的四大组件之一,也是Android最基础的东西,是非常重要的部分。搞清楚Activity的生命周期和启动模式,能够使我们设计出更流畅的程序。

本文主要记录我对Activity生命周期和启动模式的探索,从实践出发来掌握理论。

一、基础知识

1.返回栈

Android是通过返回栈来管理Activity的。每启动一个Activity,就会把该Activity放入栈中,并且处于栈顶;当去销毁一个Activity时,就会把该Activity从栈顶去除。

所以,处于界面前台并且能与用户交互的Activity一定处于返回栈的栈顶。而当按返回或finish()结束一个Activity的时候,就会显示上一个Activity。

2.生命周期

Activity一共有四种状态:

  1. 运行状态:

    处于返回栈栈顶的Activity处于运行状态。

  2. 暂停状态:

    当Activity不处于栈顶,且依然可见时就处于暂停状态。

  3. 停止状态

    当Activity不处于栈顶且不可见时处于停止状态。这时候系统可能还保留着该Activity的一些变量在内存里。

  4. 销毁状态

    当Activity从返回栈中移除之后,就变成了销毁状态。

值得注意的是暂停状态和停止状态的区别。为什么不处于栈顶的Activity还能可见?这个到后面会详细说明。

Activity一共有七个回调方法来管理生命周期:

  1. onCreate():

    在Activity被创建的时候调用。此时会完成一些初始化操作,比如加载布局绑定事件。

  2. onStart():

    在Activity由不可见变为可见的时候调用。

  3. onResume()

    当Activity准备好和用户交互的时候调用,此时的Activity一定处于栈顶

  4. onPause()

    当系统要去启动或恢复另一个Activity时调用。此时系统会释放掉一些资源,只保留一些关键数据。

  5. onStop()

    在该Activity变为不可见时调用。

  6. onDestroy()

    当一个Activity移除返回栈时调用。

  7. onRestart()

    当Activity由停止状态变为运行状态时调用。

3.启动模式

Activity一共有四种启动模式

  1. standard

    这是Activity默认的启动方式。在这个模式下,每次启动一个Activity都会创建一个新的Activity放在栈顶,无论之前返回栈中是否存在该Activity。

  2. singleTop

    在启动一个Activity的时候,系统会去判断返回栈的栈顶是不是要启动的Activity。如果是,那么就不再创建一个新的了。如果不是,那就创建一个新的Activity放到栈顶。

  3. singleTask

    这个模式下,系统会去返回栈中寻找是否有该Activity的实例。如果在站内找到了实例,就把实例前面的所有Activity都弹出栈,来使该实例处于栈顶的位置。

  4. singleInstance

    因为一个应用程序对应一个返回栈。如果一个Activity可以供多个应用程序调用,那么前面的几种方式无法做到共用。而singleInstance模式下有一个独立的栈,用来管理singleInstance模式的Activity。

前面的基础知识都是一些生涩的文字描述。想要理解比较困难,所以以下就开始了通过实践去理解这些知识。

二、实践部分

为了能够清晰地看清楚什么时候调用了生命周期回调函数,我们可以先写一个自己的BaseActivity,在生命周期回调函数里添加日志打印。后面的Activity去继承自己写的这个Activity即可。

open class BaseActivity : AppCompatActivity(){
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(this.toString(), "onCreate()")
    }
​
    override fun onStart() {
        super.onStart()
        Log.d(this.toString(), "onStart()")
    }
​
    override fun onResume() {
        super.onResume()
        Log.d(this.toString(), "onResume()")
    }
​
    override fun onPause() {
        super.onPause()
        Log.d(this.toString(), "onPause()")
    }
​
    override fun onStop() {
        super.onStop()
        Log.d(this.toString(), "onStop()")
    }
​
    override fun onDestroy() {
        super.onDestroy()
        Log.d(this.toString(), "onDestroy()")
    }
​
    override fun onRestart() {
        super.onRestart()
        Log.d(this.toString(), "onRestart()")
    }
}

1.生命周期

我准备了三个Activity:

  • LifeActivity

  • NormalActivity

  • DialogActivity

然后在LifeActivity中添加按钮,让他能够跳转到另外两个Activity去。

        btn_startNormalActivity.setOnClickListener {
            val intent = Intent(this, NormalActivity::class.java)
            startActivity(intent)
        }
        btn_startDialogActivity.setOnClickListener {
            val intent = Intent(this, DialogActivity::class.java)
            startActivity(intent)
        }

其中DialogActivity是一个对话框式的Activity。在AndroidManifest.xml去指定

<activity
 android:name=".activityStudy.DialogActivity"
 android:theme="@style/Theme.AppCompat.Dialog"
 android:label="dialogActivity"/>

其中android:theme="@style/Theme.AppCompat.Dialog"就制定了该Activity是一个对话框式的Activity。

1.1 启动和销毁LifeActivity

先启动程序看一下,发现了前三个回调函数,此为Activity的创建三部曲。

当按下返回键后,就出现了一次完整的生命周期。

1.2 启动一个普通的Activity

我们从LifeActivity中启动NormalActivity。

这里就发现,首先是LifeActivity调用了onPause()方法释放了一些资源。但此时LifeActivity依然是可见状态。当NormalActivity完成了创建三部曲之后,LifeActivity才调用了onDestroy()方法。

那我们再从NormalActivity返回试一下。

能看到,依然是当前处于栈顶的NormalActivity先调用了onPause()方法,然后等LifeActivity处于栈顶之后再调用了onStop()和onDestroy()。

这里能发现不同的是,因为LifeActivity之前并没有被销毁,而是处于停止状态,所以此时他恢复栈顶的调用顺序是onRestart() -> onStart() -> onResume()

1.3 启动一个对话框式的Activity

我们继续试一下启动对话框。启动后是这个样子

这个DialogActivity并没有沾满整个屏幕,并且LifeActivity依然处于可见状态。不过此时的LifeActivity并不能与用户交互,也就是它不在栈顶。这时的LifeActivity就处于暂停状态

查看回调函数,就会发现此时的LifeActivity并没有调用onStop()。

这也就是暂停状态和停止状态的区别。

返回后的函数调用

当LifeActivity需要回到栈顶时,此时就只需要调用onResume()函数了。

1.4 小结

Activity的创建三部曲是onCreate() onStart() onResume()

当从一个Activity A去启动Activity B的时候,会先调用A的onPause(),然后等待B创建完成。

如果B会沾满整个屏幕,使A完全不可见,那么会调用A的onStop(),使A处于停止状态。此时要恢复A时,会走onRestart() -> onStart() -> onResume()的步骤。

如果B只是一个对话框式的Activity,那么A就会处于暂停状态。要恢复A的时候,只会调用onResume()

2.启动模式

这里我们沿用LifeActivity,添加一个按钮来再启动一个LifeActivity。

    val intent = Intent(this, LifeActivity::class.java)
    startActivity(intent)

2.1 standard模式

此时LifeActivity处于默认的standard模式。我们启动试一下。

我们可以看到,这里的两个LifeActivity并不是同一个实例,而是新建了一个LifeActivity。

虽然很少有自己启动自己的操作,但是在这样的模式下会消耗很多的资源,系统里会有多个实例。

2.2 singleTop模式

那我们把LifeActivity的启动模式修改一下,改成singleTop模式。

在AndroidManifest.xml里修改。

<activity android:name=".activityStudy.LifeActivity"
          android:label="lifeActivity"
          android:launchMode="singleTop"/>

再启动试一试。

会发现无论点多少次,都只会调用onPause()和onResume()两个方法。标识也是一样的,说明是同一个实例。

那这时候我们又得搞点新操作了。

我们新添加一个MiddleActivity,可以从LifeActivity跳转到MiddleActivity。MiddleActivity也可以跳转到LifeActivity和它自己。

然后我们来进行这样的操作:LifeActivity -> MiddleActivity -> LifeActivity

C:\Users\Joker\AppData\Roaming\Typora\typora-user-images\image-20211227225906078.png

根据日志来看,确实是创建了一个新的LifeActivity,而不是同一个实例。

此时返回栈的情况应该是这样:

此时处于栈顶的LifeActivity并不是最初的那个Activity了。这也就是singleTop模式的局限性。

此时如果我们按下返回键,会返回到MiddleActivity,再次按下返回键才会返回到最初的LifeActivity。可根据上面的返回栈图片来理解。

2.3 singleTask模式

那么我们再给LifeActivity换一个启动模式。换成singleTask试一下。

这里我们可以看到LifeActivity就是同一个实例了。并且中间的MiddelActivity已经被销毁了。

此时的返回栈情况应该是这样:

所以此时我们按返回键的话,并不会返回到MiddleActivity,而是直接销毁掉这个LifeActivity了。

无论中间有多少个其他的Activity,最终都会被弹出栈。

这里我用MiddleActivity启动了6个MiddleActivity。会发现他们都被销毁了。

这样的模式虽然能够保证整个栈里只有一个实例,但是它会将上方所有Activity都弹出栈,也存在一定的风险。

2.4 singleInstance模式

这是最特殊的启动模式。一个应用程序对应一个返回栈,而singleInstance模式下的Activity会被一个额外的返回栈统一管理。

当一个Activity会被多个应用程序。

这里我用到三个新的Activity来实践:

  1. FirstActivity

  2. SecondActivity 启动模式为singleInstance

  3. ThirdActivity

然后按顺序启动他们,并在日志里打印出他们的taskId

Log.d(this.toString(), "task id is $taskId")

通过日志能够看到,FirstActivity和ThirdActivity属于同一个返回栈,而SecondActivity则属于另一个返回栈。

并且在从FirstActivity跳转到SecondActivity和从SecondActivity跳转到ThirdActivity的时候,有一个明显的类似切换应用的动画,而不是像之前的在同一应用内跳转。

并且当我从ThirdActivity点击返回后,是先返回的FirstActivity,然后再返回到了SecondActivity。并不是意料之中的返回顺序。此时是返回栈情况应该是这样:

所以当我们从ThirdActivity返回时,是按照task54来返回的。当task54空了之后,才会到task55的栈顶。

所以在这样的模式下,如果FirstActivity下面还有很多Activity,要后退到这些Activity全部销毁后才会返回到SecondActivity。所以这样的模式下跟前三个模式有很大的不同。

三、提出问题

1.为什么从停止状态恢复的Activity会调用一个onRestart()?

前面的实践可以得出结论,当Activity从暂停状态恢复到运行状态时,只需要调用onResume()。而从停止状态恢复的时候,不光要调用onStart()和onRestart(),还会先调用onRestart()。这是为什么?这个函数又做了什么事?

2.singleInstance模式下独立的栈是用singleTop模式还是singleTask模式?

既然singleInstance模式的Activity由一个统一的栈管理,这么这个栈的管理方式是singleTop还是singleTask或者是其他?

这里猜测应该是singleTask模式。毕竟要保证全局只有一个实例,如果是singleTop的话还是可能会出现多个实例。

四、探索问题

1.onRestart()调用

这部分涉及到源码分析,我会另外用一篇笔记来探究。

2.singleInstance里栈的管理模式

这里我在前面的基础上又引入了一个ForthActivity,并把启动模式设置为了singleInstance.

FirstActivity的启动模式设置为singleTop.

现在能从FirstActivity跳转到SecondActivity和ForthActivity,并且SecondActivity和ForthActivity也有跳转到FirstActivity的按钮。

于是就开始了一系列的操作。从FirstActivity跳转到SecondActivity,再跳转回FirstActivity;然后又从FirstActivity跳转到ForthActivity。本来这顿操作我以为会形成的返回栈情况是:

结果一看日志,才发现他们在各自的栈里互不打扰。

原来singleInstance的管理方式就是给这个Activity分配一个独立的栈。当有多个Activity是这个启动模式的时候,就会有多个各自的栈。这也就保证了全局唯一,并且也不用管是singleTop还是singleTask,反正栈里也只有这一个Activity。

猜你喜欢

转载自blog.csdn.net/qq_43478882/article/details/122187411