目录
6、Activity 的生命周期
(1)返回栈
Android 是使用任务(task)来管理 Activity 的,一个任务就是一组存放在栈里的 Activity 的集合,这个栈也被称作返回栈。
栈是一种后进先出的数据结构,在默认情况下,每当我们启动了一个新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish()方法去销毁一个Activity时,处于栈顶的Activity就会出栈,前一个入栈的Activity就会重新处于栈顶的位置。
系统总是会显示处于栈顶的Activity给用户。
(2) Activity 状态
每个 Activity 在其生命周期中最多可能会有四种状态。
① 运行状态
但一个 Activity 位于返回栈的栈顶时,Activity 就处于运行状态。系统最不愿意回收的就是处于运行状态的 Activity,因为这会带来非常差的用户体验。
② 暂停状态
当一个 Activity 不再处于栈顶位置,但仍然可见,Activity 就进入了暂停状态(比如对话框形式的 Activity 只会占用屏幕中间的部分区域),处于暂停状态的 Activity 是完全存活着的。
③ 停止状态
当一个 Activity不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态,有可能会被系统回收。
④ 销毁状态
一个 Activity 从返回栈中移除后就变成了销毁状态。
(3) Activity 的生存期
Activity 类中定义了7个回调方法,覆盖了 Activity 生命周期的每一个环节。
• onCreate() 这个方法在Activity第一次被创建的时候调用。
• onStart() 这个方法在Activity由不可见变为可见的时候调用。
• onResume() 这个方法在Activity准备好和用户进行交互的时候调用。
• onPause() 这个方法在系统准备去启动或者恢复另一个Activity的时候调用。
• onStop() 这个方法在Activity完全不可见的时候调用。
• onDestroy() 这个方法在Activity被销毁之前调用。
• onRestart() 这个方法在Activity由停止状态变为运行状态之前调用,也就是Activity被重新启动了。
(4)体验 Activity 的生命周期
① 新建一个 ActivityLifeCycleTest 项目
② 再创建两个子 Activity:NormalActivity 和 DialogActivity
布局命名为 normal_layout 和 dialog_layout
③ 编辑normal_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NormalActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a normal activity"/>
</LinearLayout>
④ 编辑 dialog_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DialogActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a dialog activity"/>
</LinearLayout>
⑤ 修改 AndroidManifest.xml 的<activity> 标签的配置
android:theme 属性,用于给当前 Activity 指定主题,Android 系统内置很多主题可以选择,当然也可以自己定制主题。
<activity
android:name=".DialogActivity"
android:theme="@style/Theme.AppCompat.Dialog"
android:exported="false" />
⑥ 修改 activity_main.xml,重新定制主 Activity 的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/startNormalActivity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start NormalActivity"/>
<Button
android:id="@+id/startDialogActivity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start DialogActivity"/>
</LinearLayout>
⑦ 最后修改MainActivity 中的代码
class MainActivity : AppCompatActivity() {
private val tag = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(tag,"onCreate")
setContentView(R.layout.activity_main)
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() {
super.onStart()
Log.d(tag,"onStart")
}
override fun onResume() {
super.onResume()
Log.d(tag,"onResume")
}
override fun onPause() {
super.onPause()
Log.d(tag,"onPause")
}
override fun onStop() {
super.onStop()
Log.d(tag,"onStop")
}
override fun onDestroy() {
super.onDestroy()
Log.d(tag,"onDestroy")
}
override fun onRestart() {
super.onRestart()
Log.d(tag,"onRestart")
}
}
⑧ 运行程序,观察 Logcat 中的打印日志
点击第一个按钮
按下Back键
按第二个按钮
只有 onPause( ) 方法得到了执行,onStop( ) 方法并没有执行,这是因为 DialogActivity 并没有完全遮挡注 MainActivity,此时 MainActivity 只是进入了暂停状态,并没有进入停止状态。
按下Back键也只有 onResume( )方法会得到执行
最后按下 Back 键退出程序
(5)Activity 被回收了怎么办
当一个 Activity 进入停止状态,是可能会被系统回收的。按Back 返回可能会出现前一个 Activity 临时数据被回收的情况。
Activity 中提供了一个 onSaveInstanceState( ) 回调方法,可以保证在 Activity 被回收之前一定会被调用。onSaveInstanceState()方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用 putString())方法保存字符串,使用 putInt()方法保存整型数据,以此类推。每个保存方法需要传人两个参数,第一个参数是键,用于后面从Bundle中取值,第二个参数是真正要保存的内容。
在MainActivity中添加如下代码就可以将临时数据进行保存了:
override fun onSaveInstanceState(outState: Bundle){
super.onSaveInstanceState(outState)
val tempData ="Something you just typed"
outState.putString("data key",tempData)
}
我们一直使用的 onCreate()方法其实也有一个 Bundle 类型的参数。这个参数在一般情况下都是null.但是如果在 Activity 被系统回收之前,你通过 onSaveInstanceState()方法保存数据,这个参数就会带有之前保存的全部数据,我们只需要再通过相应的取值方法将数据取出即可。
修改 MainActivity的onCreate()方法,如下所示:
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
Log.d(tag,"onCreate")
setContentView(R.layout.activity_main)
if (savedInstanceState != null){
val tempData =savedInstanceState.getString("data_key")
Log.d(tag,tempData)
}
...
}
取出值之后再做相应的恢复操作就可以了,比如将文本内容重新赋值到文本输入框上,这里我们只是简单地打印一下。
Intent还可以结合Bundle一起用于传递数据。首先我们可以把需要传递的数据都保存在 Bundle 对象中,然后再将Bundle对象存放在 Intent里。到了目标 Activity 之后,先从 intent 中取出 Bundle,再从 Bundle 中一一取出数据。
另外,当手机的屏幕发生旋转的时候,Activity也会经历一个重新创建的过程,因而在这种情况下,Activity中的数据也会丢失。虽然这个问题同样可以通过 onSaveInstanceState()方法来解决,但是一般不太建议这么做。