上篇文章都是理论知识,下面我们将通过一个示例,更加直观的体验活动的生命周期
项目代码
这样主活动使用默认方法创建完成,我们还需要分别再创建两个子活动,NormalActivity 和 DialogActivity。
修改activity_normal.xml 文件,代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a normal activity create by weber.pub"
/>
</LinearLayout>
修改activity_dialog.xml 文件,代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a dialog activity create by bym"
/>
</LinearLayout>
修改 NormalActivity 文件,代码如下:
public class NormalActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_normal);
}
}
修改 DialogActivity 文件,代码如下:
public class DialogActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_dialog);
}
}
修改 AndroidManifest.xml 文件,代码如下:
<activity android:name=".NormalActivity" />
<activity android:name=".DialogActivity" android:theme="@android:style/Theme.Dialog">
</activity>
这里分别为两个活动进行注册,但是 DialogActivity 的注册代码有些不同,它使用了一个 android:theme 属性,这是用于给当前活动指定主题的,Android 系统内置有很多主题可以选择,当然我们也可以定制自己的主题,而这里@android:style/Theme.Dialog 则毫无疑问是让 DialogActivity 使用对话框式的主题。
修改 activity_main.xml 文件,代码如下:
<?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:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="pub.weber.bym.activitylifecycletest.MainActivity">
<Button
android:id="@+id/start_normal_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击启动 NormalActivity" />
<Button
android:id="@+id/start_dialog_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击启动 DialogActivity" />
</LinearLayout>
最后修改 MainActivity 中的代码,如下所示:
public static final String TAG = "MainActivity --->>>> ";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"onCreate");
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
Button startNormalActivity = (Button) findViewById(R.id.start_normal_activity);
final Button startDialogActivity = (Button) findViewById(R.id.start_dialog_activity);
startNormalActivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,NormalActivity.class);
startActivity(intent);
}
});
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");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart");
}
在 onCreate()
方法中,我们分别为两个按钮注册了点击事件,点击第一个按钮会启动NormalActivity,点击第二个按钮会启动 DialogActivity。然后在 Activity 的七个回调方法中分别打印了一句话,这样就可以通过观察日志的方式来更直观地理解活动的生命周期。
程序运行结果分析
运行程序:
这时可以看到日志:
可以看到,当MainActivity第一次被创建时会依次执行onCreate()
、onStart()
和onResume()
方法。然后点击第一个按钮,启动 NormalActivity,如图所示。
由于 NormalActivity 已经把 MainActivity 完全遮挡住,因此 onPause()
和 onStop()
方法都会得到执行。
然后按下 Back 键返回 MainActivity,打印信息如下图所示。
由于之前 MainActivity 已经进入了停止状态,所以 onRestart()
方法会得到执行,之后又会依次执行onStart()
和onResume()
方法。注意此时onCreate()
方法不会执行,因为MainActivity并没有重新创建。
然后再点击第二个按钮,启动 DialogActivity
可以看到,只有 onPause()
方法得到了执行,onStop()
方法并没有执行,这是因为DialogActivity 并没有完全遮挡住 MainActivity,此时 MainActivity 只是进入了暂停状态,并没有进入停止状态。相应地,按下 Back 键返回 MainActivity 也应该只有 onResume()
方法会得到执行,如图所示。
最后在 MainActivity 按下 Back 键退出程序,打印信息如图
依次会执行 onPause()、onStop()和 onDestroy()方法,最终销毁 MainActivity。
活动被回收了怎么办
前面我们已经说过,当一个活动进入到了停止状态,是有可能被系统回收的。那么想象以下场景,应用中有一个活动 A,用户在活动 A 的基础上启动了活动 B,活动 A 就进入了停止状态,这个时候由于系统内存不足,将活动 A 回收掉了,然后用户按下 Back 键返回活动A,会出现什么情况呢?其实还是会正常显示活动 A的,只不过这时并不会执行 onRestart()方法,而是会执行活动 A 的 onCreate()方法,因为活动 A 在这种情况下会被重新创建一次。
这样看上去好像一切正常,可是别忽略了一个重要问题,活动 A 中是可能存在临时数据和状态的。打个比方,MainActivity 中有一个文本输入框,现在你输入了一段文字,然后启动 NormalActivity,这时 MainActivity 由于系统内存不足被回收掉,过了一会你又点击了Back 键回到 MainActivity,你会发现刚刚输入的文字全部都没了,因为 MainActivity 被重新创建了。
Activity 中还提供了一个 onSaveInstanceState()回调方法,这个方法会保证一定在活动被回收之前调用,因此我们可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。
onSaveInstanceState()
方法会携带一个 Bundle 类型的参数,Bundle 提供了一系列的方法用于保存数据,比如可以使用 putString()
方法保存字符串,使用 putInt()
方法保存整型数据,以此类推。每个保存方法需要传入两个参数,第一个参数是键,用于后面从 Bundle中取值,第二个参数是真正要保存的内容。
在 MainActivity 中添加如下代码就可以将临时数据进行保存:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "Something you just typed";
outState.putString("data_key", tempData);
}
数据是已经保存下来了,那么我们应该在哪里进行恢复呢?我们使用的 onCreate()方法其实也有一个 Bundle类型的参数。这个参数在一般情况下都是null,但是当活动被系统回收之前有通过 onSaveInstanceState()方法来保存数据的话,这个参数就会带有之前所保存的全部数据,我们只需要再通过相应的取值方法将数据取出即可。
修改 MainActivity 的 onCreate()方法,如下所示:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG, tempData);
}
}
取出值之后再做相应的恢复操作就可以了,比如说将文本内容重新赋值到文本输入框上,这里我们只是简单地打印一下。