Android学习之活动的生命周期
•返回栈
Android 中的活动是可以叠层的,我们每启动一个新的活动,就会覆盖在原活动之上,
然后点击 Back 键会销毁最上面的活动,下面一个活动就会重新显示出来;
其实 Android 是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack);
栈是一种后进先出的数据结构,在默认情况下,每当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置;
而每当我们按下 Back 键或者调用 finish() 方法去销毁一个活动时,处于栈顶的活动会出栈,
这时,前一个入栈的活动就会重新处于栈顶位置,系统总是会显示栈顶的活动给用户。
•活动的状态
- 运行状态
- 当一个活动位于返回栈的栈顶时,这时活动就处于运行状态
- 系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验
暂停状态
- 当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态
- 比如对话框形式的活动只会占用屏幕中间的部分区域,你还可以看到后边的界面
- 这时,后面的活动就处于暂停状态
停止状态
- 当一个活动不在处于栈顶位置,并且完全处于不可见的时候,就进入了停止状态
- 系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的
- 当其他地方需要内存时,处于停止状态的活动有可能会被系统回收
销毁状态
- 当一个活动从返回栈中移除后就变成了销毁状态
- 系统倾向于回收处于这种状态的活动,从而保证手机内存充足
•活动的生存期
Activity 类中定义了七个回调方法,覆盖了活动生命周期的每一个环节;
onCreate()
- 每个活动中都重写这个方法,他会在活动第一次被创建的时候调用
- 你应该在这个方法中完成活动的初始化操作,比如加载布局、绑定事件等
onStart()
- 这个方法在活动由不可见变为可见的时候调用
onResume()
- 这个方法在活动准备好和用户进行交互时调用
- 此时的活动一定位于返回栈的栈顶,并且处于运行状态
onPause()
- 这个方法在系统准备去启动或者恢复另一个活动的时候调用
- 我们通常会在这个方法中将一些消耗 CPU 的资源释放掉以及保存一些关键数据
onStop()
- 这个方法在活动完全不可见的时候调用
- 它和 onPause() 方法的区别在于,如果启动的新活动是一个对话框样式的活动
- 那么 onPause() 方法会执行, onStop() 方法不会执行
onDestroy()
- 这个方法在活动被销毁之前调用
- 之后活动的状态将变为销毁状态
onRestart()
- 这个方法在活动由停止状态变为运行状态之前调用
- 也就是活动被重新启动了
以上七个方法除了 onRestart() 方法,其他都是两两相对的,从而又可以将活动分为三种生存期;
完整生存期
- 活动在 onCreate() 方法和 onDestroy() 方法之间所经历的就是完整生存期
- 一般情况下,一个活动会在 onCreate() 方法中完成各种初始化操作
- 而在 onDestroy() 方法中完成释放内存的操作
可见生存期
- 活动在 onStart() 方法和 onStop() 之间所经历的就是可见生存期
- 在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互
前台生存期
- 活动在 onResume() 方法和 onPause() 方法之间所经历的就是前台生存期
- 这个生存期内,活动总是处于运行状态
- 此时的活动是可以和用户进行相互的,我们平时看到和接触最多的也是这个状态下的活动
活动生命周期示意图
•体验活动的生命周期
准备工作
新建一个项目,命名为 ActivityLifeCycleTest,并选择 Empty Activity;
进入项目,将模式结构改成 Project 结构;
接下来,我们需要额外新建两个 Empty Activity;
右击 com.example.activitylifecycle -> New -> Activity -> Empty Activity ;
新建的两个 Activity 分别命名为 NormalActivity 和 DialogActivity;
接下来就是敲代码的环节了;
activity_normal.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="this is a normal activity" android:textSize="20sp" android:layout_centerInParent="true"/> </RelativeLayout>activity_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="this is a dialog activity" android:textSize="20sp" android:layout_centerInParent="true"/> </RelativeLayout>通过代码可以看出,这两个布局文件的代码几乎没有区别,只是显示的文字不同;
其实从名字上可以看出,这两个 Activity 一个表示普通活动,一个表示对话框活动;
但我们并没有对 .java 文件任何修改,在哪里体现出 DialogActivity 为对话框活动呢?
别着急,下面我们马上开始配置;
打开 AndroidManifest.xml 文件;
有没有发现什么不一样的?
没错,在 .DialogActivity 的配置中;
通过使用 android:theme="@style/Theme.AppCompat.Dialog" 将 DialogActivity 设置成对话框形式的主题;
接下来,我们修改 activity_main.xml 中的布局;
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" android:orientation="vertical"> <Button android:id="@+id/btn_1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start Normal Activity" android:textAllCaps="false"/> <Button android:id="@+id/btn_2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start Dialog Activity" android:textAllCaps="false"/> </LinearLayout>可以看到,在该布局中加入了两个按钮;
btn_1 用于启动 NormalActivity,btn_2 用于启动 DialogActivity;
最后修改 MainActivity.java 中的代码;
MainActivity.java
public class MainActivity extends AppCompatActivity { public static final String TAG = "MainActivity"; private Button btn1; private Button btn2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "onCreate: "); btn1 = findViewById(R.id.btn_1); btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,NormalActivity.class); startActivity(intent); } }); btn2 = findViewById(R.id.btn_2); btn2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,DialogActivity.class); startActivity(intent); } }); } @Override protected void onStart() { super.onStart(); Log.d(TAG, "onStart: "); } @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume: "); } @Override protected void onPause() { super.onPause(); Log.d(TAG, "onPause: "); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "onRestart: "); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy: "); } @Override protected void onStop() { super.onStop(); Log.d(TAG, "onStop: "); } }在 onCreate() 方法中,分别为两个按钮注册了点击事件,用于启动 NormalActivity 和 DialogActivity;
然后在 Activity 的 7 个回调方法中分别打印一句话,这样我们就可以通过观察日志的方式来更直观的理解活动的生命周期;
有关 Log 的用法,请参考我的这篇博客🔗;
运行程序
这时,观察 logcat 中的打印语句;
可以看到,当 MainActivity 第一次被创建的时候,会依次执行 onCreate() , onStart() 和 onResume() 方法;
然后,点击第一个按钮,启动 NomalActivity;
启动 NomalActivity
观察 logcat 中的打印语句;
由于 NormalActivity 已经把 MainActivity 完全遮住了,因此 onPause() 方法和 onStop() 方法都会执行;
然后,按下 Back 键返回,观察 logcat 中的打印语句;
由于之前 MainActivity 进入了停止状态,所以 onRestart() 方法会执行;
之后又依次开始执行 onStart() 方法和 onResume() 方法;
注意此时不会执行 onCreate() 方法,因为 MainActivity 并没有被重新创建;
然后,点击第二个按钮启动 DialogActivity;
启动DialogActivity
观察 logcat 中的打印语句;
可以看到,只有 onPause() 方法得到了执行, onStop() 方法并没有执行;
这是因为 DialogActivity 并没有完全遮挡住 MainActivity,此时,MainActivity 只是进入了暂停状态,并没有进入到停止状态;
相应的,按下 Back 键返回,也应该只有 onResume() 方法执行;
最后在 MainActivity 下按 Back 键退出程序,打印信息如下所示;
依次会执行 onPausr() , onStop() , onDestroy() 方法,最终销毁 MainActivity;