一. Activity生命周期
1.1 返回栈知识点
二. Activity状态
2.1 启动状态
2.2 运行状态
2.3 暂停状态
2.4 停止状态
2.5 销毁状态
三. Activity生存期
3.1 回调方法
3.2 生存期
四. 体验Activity的生命周期
五. Activity被回收办法
引言:
掌握Activity的生命周期对Android开发来说非常重要,当我们深入理解Activity的生命周期之后,就可以写出更加连贯流畅的理序,并在如何合理管理应用资源方面发挥得游刃有余。我们的应用程序也将会拥有更好的用户体验。
一**.Activity****生命周期**
1.1****返回栈:
我们知道,Android里的Activity可以层叠的,我们每启动一个新的Activity,就会覆盖在原来的Activity之上,然后点击Back按键,会销毁最上面的Activity,下面的一个Activity就会显示出来。
其实Android是使用任务(task)来管理Activity的,一个任务就是一组存放在栈里的Activity的集合,这个栈也被称作返回栈(back stack)。栈是一种后进先出的数据结构,在默认情况下,每当我们启动了一个新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish()方法去销毁一个Activity时,处于栈顶的Activity就会出栈,前一个入栈的Activity就会重新处于栈顶的位置。
系统总是会显示处于栈顶的Activity给用户。
![](https://i-blog.csdnimg.cn/direct/61d0ab4119f5484389342d7e21550b22.png)
图1:返回栈流程图
1.2 Activity****状态
Activity有主要的四个状态,以及一个非常迅速的启动状态,下面我们来讲解他们的功能:
1.2.1启动状态(Starting)****:
启动状态就是内个非常迅速的状态,在Activity启动时会自动跳转到下一个状态,这也就是为什么很多的Activity介绍里只写了四个状态而没有介绍这个状态了。
1.2.2运行状态(Running)****:
当一个Activity位于返回栈的栈顶时,Activity就处于运行状态。系统最不愿意回收的就是处于运行状态的Activity,因为这会带来非常差的运行体验。
1.2.3暂停状态(Paused)****:
当一个Activity不再处于栈顶的位置,但仍然可见时,Activity就进入了暂停状态。你可能会觉得,既然Activity已经不在栈顶了,怎么会可见呢?这是因为并不是每一个Activity都会占满整个屏幕,比如对话框式的Activity只会占用中间屏幕的部分区域。处于暂停状态的Activity仍然是完全存活的,系统也不愿意回收这种Activity(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种Activity。
1.2.4停止状态(Stopped):
当一个Activity 不再处于栈顶位置,并且完全不可见的时候,就进人了停止状态。系统仍然会为这种Activity保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的Activity有可能会被系统回收。
1.2.5销毁状态(Destroyed):
一个Activity 从返回栈中移除后就变成了销毁状态。系统最倾向于回收处于这种状态的Activity以保证手机的内存充足。
图 2 :状态关系图
1.3****生存期
1.3.1****回调方法
在Activity类中定义了7个回调方法,其覆盖了Activity生命周期的每一个环节,下面我们来一个一个看这 些方法:
**1.onCreate()------**创建
这个方法我们已经看到过很多次了,在每个Activity 中都重写了这个方法,它会在Activity第一次被创建的时候调用。我们应该在这个方法中完成Activity的初始化操作,比如加载布局、绑定事件等。
**2.onStart()------**启动
这个方法在Activity由不可见变为可见的时候调用
**3.onResume()------**恢复
这个方法在Activity准备好和用户进行交互的时候调用。此时的Activity一定位于返回栈的栈顶,并且处于运行状态。
**4.onPause()------**停顿
这个方法在系统准备去启动或者恢复另一个Activity的时候调用。我们通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶Activity的使用。
**5.onStop()------**暂停
这个方法在Activity完全不可见的时候调用。它和onPause()方法的主要区别在于,如果启动的新Activity是一个对话框式的Activity,那么onPause()方法会得到执行,而onstop()方法并不会执行。
**6.onDestory()------**销毁
这个方法在Activity被销毁之前调用,之后Activity的状态将变为销毁状态。
**7.onRestart------**重启
这个方法在Activity由停止状态变为运行状态之前调用,也就是Activity被重新启动了。
1.3.2****生存期
1.3.2.1****完整生存期
完整生存期:Activity在onCreate()方法和onDestroy()方法之间所经历的就是完整生存期。一般情况下, 一个Activity会在onCreate()方法中完成各种初始化操作,而在onDestroy()方法中完成释放内存的操 作。
1.3.2.2****可见生存期
Activity在onStart()方法和onStop()方法之间所经历的就是可见生存期。在可见生存期内,Activity对于用户总是可见的,即便有可能无法和用户进行交互。我们可以通过这两个方法合理地管理那些对用户可见的资源。比如在onStart()方法中,对资源进行加载,而onStop()方法中对资源进行释放,从而保证处于停止状态的Activity不会占用过多内存。
1.3.2.3****前台生存期
Activity在onResume()方法和onPause()方法之间所经历的就是前台生存期。在前台生存期内, Actvity总是处于运行状态,此时的 Activity 是可以和用户进行交互的,我们平时看到和接触最多的就是这个状态下的Activity。
![](https://i-blog.csdnimg.cn/direct/72c70eb339ae4b2bbe75ff043949fcc5.png)
图3.Activity生命周期
1.4体验Activity****的生命周期
了解了Activity的生命周期之后,我们来写一个实例,通过实例来详细了解。
首先,我们新建一个EmptyActivity,名为NormalActivity,其布局起名为normal_layout;使用相同的方法创建DialogActivity,布局起名为dialog_layout。
接下来,我们先编辑normal_layout.xml文件。
<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>
在这里我们简单的定义了一个TextView,显示了一行文字"This is a normal activity"。
接着我们编辑dialog_layout.xml文件,代码如下:
<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>
接下来我们修改AndroidManifest.xml的标签配件。
< activity
android : name = ".DialogActivity"
android : theme = "@style/Animation.Design.BottomSheetDialog" >
</ activity >
< activity
android : name = ".NormalActivity"
android : exported = "false" />
这里注册了两个Activity,此时我们为什么使用了一个android:theme的属性?这里是给前面的Activity指定主题,这里的"@style/Animation.Design.BottomSheetDialog"则毫无疑问是让DialogActivity使用对话框的主题。现在,我们可以修改activity_main.xml:
<?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="wrap_content">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/startNormalActivity"
android:text="Start NormalActivity"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/startDialogActivity"
android:text="Start DialogActivity"/>
</LinearLayout>
这里我们添加两个按钮,一个用于启动NormalActivity,一个用于启动DialogActivity,修改代码:
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
setContentView(R.layout.activity_main);
findViewById(R.id.startNormalActivity).setOnClickListener(new
View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,
NormalActivity.class);
startActivity(intent);
}
});
findViewById(R.id.startDialogActivity).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 onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
现在,我们启动程序,并看一下logcat的效果。
![](https://i-blog.csdnimg.cn/direct/2a3d582abea34b2cb3394c3c733b52f9.png)
图4:Main界面图
此时我们不对界面做任何操作,看log cat运行过程:
![](https://i-blog.csdnimg.cn/direct/1a64f57394c54aed887cc592dfaf9755.png)
图5:MainLogCat打印日志
这里其实我们就会发现,在MainActivity第一次被创建时会依次执行onCreate,onStart和onResume方法。
然后点击第一个按钮NormalActivity,打开normal_layout界面:
![](https://i-blog.csdnimg.cn/direct/5fcc3d86e8e14c06ba2d710470faff15.png)
图6:NormalActivity界面
此时的Logcat打印日志:
![](https://i-blog.csdnimg.cn/direct/fbc2a6c8151c476db3fd2b47ab8614ca.png)
图7:NormalActivity的LogCat打印日志
由于此时NormalActivity已经把MainActivity完全遮挡住,所以onPause和onStop都会执行。然后按下Back按键返回MainActivity。
这时候我们返回原来的界面,再看log cat打印日志:
![](https://i-blog.csdnimg.cn/direct/825d6bc2e9974b06a1d69894c6ea87d8.png)
图8:返回MainActivity时的打印日志
由于之前的MainActivity已经进入了停止状态,所以onRestart方法会得到执行,之后会依次执行
onRestart和onResume方法,此时onCreate方法不会执行,因为MainActivity并没有重新创建。
点击第二个按钮,启动DialogActivity。
此时的logcat:
![](https://i-blog.csdnimg.cn/direct/34b757c959064255ac2b5aa3028882ad.png)
图9:打开DialogActivity界面
可以看到,只有onPause的方法得到了执行,onStop没有得到执行,这是因为DialogActivity并没有完全遮挡住MainActivity,此时MainActivity只是进入暂停状态,并没有进入停止状态。
最后在MainActivity按下Back按键,退出程序,打印程序:
![](https://i-blog.csdnimg.cn/direct/0d58393a92f94ce29d1de80601da6f60.png)
图10:结束界面
1.5Activity 被回收办法
前面讲过,当一个Activity进入了停止状态,是有可能被系统回收的。我们可以假设一种情况:有一个ActivityA,我们在这个基础上启动了ActivityB,这时候A任务就陷入停止状态,这个时候由于系统的内存不足,系统将ActivityA回收掉了,然后用户按下Back按键返回ActivityA,正常情况下还是会显示ActivityA,这是并不会执行onRestart方法,而是会执行onCreate方法,因为A在这种情况下被重新创建一次。
但是偶尔我们还是回遇见一种情况,如果在A中可能会存在临时数据和状态,比如说A里有一个文本输入框,我们输入了一段数据;拿上面内个程序举例,此时我们打开了NormalActivity,这时MainActivity由于内存不足被回收掉,此时我们如果点击Back返回;此时如果内存不足的话,刚刚输入的文字就都没了,因为MainActivity被重建了。
这种情况是非常影响用户体验的,这时Activity里有一种回调方法我们就可以使用了------onSaveInstanceState(),这个方法可以保证在Activity被回收之前一定会被调用。
omSaveInstanceState()方法携带一个Bundle类的参数,Bundle提供一系列的方法用于保存数据,比如可以使用putString()方法保存字符串,使用putInt()方法保存整型数据,以此类推。每个保存方法需传入两个参数,第一个参数是键,用于后面的Bundle中取值,第二个参数是真正要保存到内容。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "Something you just typed";
outState.putString("data_key", tempData);
}
数据已经保存下来了,那么我们在哪里恢复呢?我们一直使用的onCreate方法其实也有一个Bundle类型的参数。这个参数在一般情况下都是null。但是如果在Activity被系统回收之前,我们通过 onSaveInstanceState方法保存数据,这个参数就会带有之前的保存的数据,我们只需要通过相应的取值方法将数据去除,修改代码如下:
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MyActivity extends AppCompatActivity {
private static final String TAG = "MyActivity"; // 确保你有一个合适的TAG
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG, "tempData is " + tempData);
}
}
}
取出值之后再做相应的恢复操作就可以了。
我们会发现在使用Bundle保存和取出数据的时候和使用Intent传递 也有类似的方法。这里Intent还可以结合Bundle一起用于传递数据。
首先我们可以把传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里,到了目标的Activity之后,先从Intent中取出Bundle,再从Bundle中一一取出数据。
注:另外,当手机的屏幕旋转的时候, Activity 也会有一个重建的过程,所以这时 候,数据也可能发生丢失的情况。