3.4.1 返回栈
Android 是使用任务(task) 来管理Activity 的,一个任务就是一组存放在栈里的Activity的集合,这个栈也被称作返回栈(back task)。栈是一种先进后出的数据结构,在默认情况下每当我们启动了一个新的 Activity ,它就会在返回栈中入栈,并处于栈顶位置。每当我们按下Back 键或者调用 finish() 方法去销毁一个Activity 时,处于栈顶的Activity 就会出栈,前一个入栈的Activity 就会重新处于栈顶的位置。系统总是会显示处于栈顶的Activity 给用户。
3.4.2 Activity 状态
每个Activity 在其生命周期中最多可能会有4个状态。
1、运行状态:位于返回栈栈顶。系统基本不会回收这个Activity。
2、暂停状态:不在栈顶,但是可见!内存极低的情况系统会回收。
3、停止状态:不在栈顶,完全不可见。系统需要内存时候有可能回收,不太稳定。
4、销毁状态:从返回栈移除后,就变成销毁状态,系统最倾向优先回收这种状态的Activity。
3.4.3 Activity 的生命周期
Activity类定义了7个回调方法,覆盖了生命周期的每个环节:
onCreate:第一次创建Activity 调用。
onStart:由不可见变为可见调用。
onResume:Activity 位于栈顶,而且处于运行状态调用。
onPause:系统准备去启动或者恢复另一个Activity 的时候调用,可以用来释放CPU 资源,以及保存关键数据,但是要保证执行速度,否则会影响用户体验和新的栈顶Activity 的使用。
onStop:完全不可见,如果启动的新Activity是对话框Activity,那么这个方法不会调用,因为对话框Activity 不会完全遮挡屏幕。
onDestroy:被销毁之前调用。之后Activity 状态进入被销毁状态。可以用来释放资源。
onRestart:由停止状态变为运行状态之前调用,会调用onStart 依次排列调用,在完全不可见状态下恢复就会调用。
如果Activity 被杀掉进程后想要恢复调用,那么就会直接进入onCreate。
Activity 可以分为三种生存期:
完整生存期 onCreate --> onDestroy 生存到销毁。
可见生存期 onStart ~ onStop 之间就是可见的,但是还不能和用户交互。
前台生存期 onResume ~ onPause 之间是可见的,可以和用户交互。
3.4.4 体验Activity 的生命周期
我们新建个demo 进行演示。
首先我们创建一个ActivityLifeCyeleTest 项目,然后再创建两个Activity ,分别为NormalActivity 和DialogActivity 。DialogActivity 设置主题为Dialog 对话框Activity。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activitylifecycletest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".DialogActivity"
android:theme="@style/Theme.AppCompat.Dialog"
android:label="DialogActivity"
></activity>
<activity android:name=".NormalActivity"
android:label="NormalActivity"
/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
我们在MainActivity 的布局文件中添加两个 Button 按钮进行做每个Activity 的跳转事件。然后重写声明周期方法,并且通过Log 打印出来:
class MainActivity : AppCompatActivity() {
private val tag = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d(tag,"onCreate")
startNormalActivity.setOnClickListener {
val intent = Intent(this,NormalActivity::class.java)
startActivity(intent)
}
startDialogActivity.setOnClickListener {
val intent = Intent(this,DialogActivity::class.java)
startActivity(intent)
}
}
override fun onStart() {
Log.d(tag,"onStart")
super.onStart()
}
override fun onRestart() {
Log.d(tag,"onRestart")
super.onRestart()
}
override fun onResume() {
Log.d(tag,"onResume")
super.onResume()
}
override fun onPause() {
Log.d(tag,"onPause")
super.onPause()
}
override fun onStop() {
Log.d(tag,"onStop")
super.onStop()
}
override fun onDestroy() {
Log.d(tag,"onDestroy")
super.onDestroy()
}
}
这里我们就可以用来测试和打印生命周期的顺序了。当MainActivity 第一次运行的时候执行的生命周期顺序为,onCreate -- onStart -- onResume,当我们点击Start NormalActivity 按钮的时候MainActivity 会执行 onPause 和 onStop 方法,因为NormalActivity 启动会直接遮挡MainActivity 所以会调用onStop ,当我们使用Back 键返回的时候,MainActivity 会调用onRestart -- onStart -- onResume ,如果我们启动了DialogActivity ,我们的MainActivity 会只调用onPause 因为DialogActivity 并不是全屏显示的,我们还可以看见MainActivity 所以MainActivity 不会执行onStop 方法。最后按下Back 结束应用。MainActivity 会执行,onPause -- onStop -- onDestroy
3.4.5 Activity 被回收了怎么办
当一个Activity 进入停止onStop 状态后是可能被系统回收的。如果从 Activity A 进入B ,此时A被系统回收,这个时候按下Back 按键会出现什么情况?这个时候就会从A 的onCreate 方法开始重新创建一次。
如果这个页面有用户的临时数据怎么办?重新创建就意味着临时数据的丢失,是会比较影响用户体验的,在Activity 中还提供了一个onSaveInstanceState 回调方法。这个回调方法可以保证在Activity 被回收之前一定被调用。onSaveInstanceStata会携带一个Bundle 的参数,我们可以把数据存放在Bundle 对象中,存储方式是键值对。
override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
super.onSaveInstanceState(outState, outPersistentState)
val tempData = "Something you just typed"
outState.putString("data_key",tempData)
}
这个方式存储的数据我们要怎么获取呢?就是onCreate 回调中的Bundle 类型的参数。如果触发了 onSaveInstanceState 后恢复了Activity 那么onCreate 中的 Bundle 参数就不会是空的!我们就可以拿到我们之前存储的数据。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d(tag,"onCreate")
savedInstanceState?.let {
val tempData = it.getString("data_key")
Log.d(tag,tempData)
}
}
这里要说明一下,手机旋转也会触发Activity 的重新创建过程,Activity 的数据也会丢失。虽然可以通过这种方式进行保存数据,但是我们会有更高级的方式来解决这个问题,这个在后面会说。