Android中的Acivity是可以层叠的,每启动一个新的Activity,就会覆盖在原Activity之上,然后点击back键就会销毁最上面的Activity,下面的一个Activity就会重新显示出来。
Android是使用任务(Task)来管理Activity的,一个任务就是一组存放在栈中的Activity的集合,该栈也称为返回栈(back stack)。栈是一种后进先出的数据结构,默认情况下,每当启动一个新的Activity,其就会在返回栈中入栈,并处于栈顶的位置,而每当按下back键或调用finish方法销毁一个Activity时,处于栈顶的Activity就会出栈,前一个入栈的Activity就会重新处于栈顶的位置。
系统总是会显示处于栈顶的Activity给用户。
每个Activity在其生命周期中最多可能会有四种状态。
Activity类定义了7个回调方法,覆盖了Activity生命周期的每一个环节:
上面几个方法中,除了onRestart方法,其它都是成对的,从而又可以将Activity分为以下3中生存期:
这里新建一个项目,命名ActivityLifeCycleTest,并允许自动创建Activity和布局,然后再创建两个子Activity,分别命名为NormalActivity和DialogActivity。修改normal_layout为:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="This is a normal activity" /> </LinearLayout>
修改dialog_layout为:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="This is a dialog activity" /> </LinearLayout>
两个内容差不多,都是使用TextView显示一行文字。
修改AndroidManifest.xml文件将DialogActivity配置为对话框式:
<activity android:name=".DialogActivity" android:exported="true" android:theme="@style/Theme.AppCompat.Dialog" /> <activity android:name=".NormalActivity" android:exported="true" />
上面使用android:theme属性指定了该Activity的主题。
修改activity_main布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/startNormalActivity" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start NormalActivity" tools:ignore="DuplicateIds" /> <Button android:id="@+id/startDialogActivity" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start DialogActivity"/> </LinearLayout>
上面添加了两个按钮,一个用于启动NormalActivity,一个用于启动DialogActivity。
最后修改MainActivity中的代码:
package com.example.activitylifecycletest import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import kotlinx.android.synthetic.main.activity_main.* 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") } }
在onCreate方法中,分别为两个按钮注册了点击事件,点击第一个按钮会启动NormalActivity,点击第二个按钮会启动DialogActivity,然后覆写之前提到的几个方法。
运行程序后的结果为:
对应出现的打印信息为:
2022-09-12 13:50:36.745 11707-11707/com.example.activitylifecycletest D/MainActivity: onCreate 2022-09-12 13:50:37.291 11707-11707/com.example.activitylifecycletest D/MainActivity: onStart 2022-09-12 13:50:37.295 11707-11707/com.example.activitylifecycletest D/MainActivity: onResume
可以看出,当MainActivity第一次被创建时会一次执行onCreate,onStart和onResume方法,然后点击第一个按钮,启动NormalActivity:
此时打印信息为:
2022-09-12 13:52:34.493 11707-11707/com.example.activitylifecycletest D/MainActivity: onPause 2022-09-12 13:52:35.455 11707-11707/com.example.activitylifecycletest D/MainActivity: onStop
此时NormalActivity已经把MainActivity完全遮挡住,因此会执行onPause和onStop方法,然后按下Back键返回MainActivity,打印信息为:
2022-09-12 13:54:37.474 11707-11707/com.example.activitylifecycletest D/MainActivity: onRestart 2022-09-12 13:54:37.486 11707-11707/com.example.activitylifecycletest D/MainActivity: onStart 2022-09-12 13:54:37.488 11707-11707/com.example.activitylifecycletest D/MainActivity: onResume
由于之前MainActivity已经进入了停止状态,所以会执行onRestart犯法,然后会执行onStart和onResume方法。
然后点击第二个按钮,启动DialogActivity:
此时打印信息为:
2022-09-12 13:56:23.189 11707-11707/com.example.activitylifecycletest D/MainActivity: onPause
可以看到,此时只执行了onPause方法,这是因为DialogActivity并没有完全遮挡主MainActivity,此时MainActivity进入了暂停状态,并没有进入停止状态。
按下back后的打印信息为:
2022-09-12 13:59:24.829 11707-11707/com.example.activitylifecycletest D/MainActivity: onResume
此时也只有onResume方法执行,最后按back推出MainActivity后的打印信息为:
2022-09-12 14:00:22.132 11707-11707/com.example.activitylifecycletest D/MainActivity: onPause 2022-09-12 14:00:23.358 11707-11707/com.example.activitylifecycletest D/MainActivity: onStop 2022-09-12 14:00:23.377 11707-11707/com.example.activitylifecycletest D/MainActivity: onDestroy
依此会执行onPause,onStop和onDestroy方法,最终销毁MainActivity。
之前提到,当一个Activity进入了停止状态,是有可能被系统回收的。而假设存在这样的场景,应用中存在一个Activity A,用户在Activity A的基础之上启动了Activity B,A就进入了停止状态,而由于系统内存的问题,A被回收,此时用户按下back返回A,此时便会调用onCreate方法而不是onRestart方法,重新创建Activity A进行显示。
上边这种情况看起来很合理,但是如果在Activity A中存在用户输入的信息或处理后的数据,那么此时回收Activity A后调用onCreate方法重建便会丢失这些内容,这是很影响用户体验的。
而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方法保存数据,该参数就会带有之前保存的全部数据,只是需要使用对应的取值方法获取数据即可。
if (savedInstanceState != null) { val tempData = savedInstanceState.getString("data_key") Log.d(tag, "tempData is $tempData") }
拿到数据之后,进行对应的处理即可。
下一个:CentOS 7.5 配置