Activity及其生命周期小结

时间:2021-03-29 14:45:12

Activity及其生命周期方法

△概述

Activity,是安卓里与用户交互的组件,通俗点说平时用手机的时候看到的一个个界面就是“Activity"所组成的(当然也有可能会是Fragment,这个不在本文讨论),Activity类似一个容器,可以装填布局文件,装填可显示的东西,但Activity本身不具备绘图功能。


△创建"Activity"

要创建一个可用的Activity,一共是有两个方式,在集成开发环境里(如Android Studio)在你想创建Activity的路径哪里右键→new→Activity,很简单的就完成创建了,如果你不想系统来帮你创建,想要自己一步一步来做,那么下面就是步骤:

(1)创建一个Java类,继承Activity,也可继承Activity子类(如FragmentActivity、ListActivity)。

(2)在"Manifest.xml"文件声明你的Activity。

(3)新建一个布局文件,该布局用作该Activity的界面。

(4)重写Activity里的onCreate()方法,调用"setContentView()"方法将布局文件与Activity绑定。 

下面看看具体代码:

(1)创建一个Java类,类名叫做"TestActivity",继承Activity,比如这样:

public class TestActivity extends Activity{

}
这里只是创建了一个类,其他代码稍后补上。

(2)在"Manifest.xml"文件里面声明你的Activity,像这样子:

<activity android:name=".TestActivity">
<!--<intent-filter>-->
<!--<action android:name="android.intent.action.MAIN" />-->

<!--<category android:name="android.intent.category.LAUNCHER" />-->
<!--</intent-filter>-->
</activity>

注意注释掉的部分,如果你的Activity是用作你应用启动时的入口(就是首个Activity),就要写上这几行了,否则的话不要写上。
(3)新建一个布局文件,将它作为Activity界面,这里布局文件的名字是:activity_test,像这样子:

<?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="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:text="TestActivity"/>
</LinearLayout>
这里面只添加一个文本,文本显示一段文字。

(4)重写Activity里的onCreate()方法,调用"setContentView()"方法将布局文件与Activity绑定。 

public class TestActivity extends Activity{

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
}
}

相比第一步的代码,就是重写一个生命周期方法,然后将布局文件给设置上去。


△启动"Activity"

前面讲了怎么创建一个Activity,那么创建好之后该怎么用呢,现在来看怎么启动Activity。启动Activity也有不同方式,如果一个Activity是应用的入口,那么不用你去启动,系统启动应用那时自动启动,如果一个Activity不是应用入口,那么根据是否需要被启动的"Activity"在结束后返回数据,可以调用两个不同方法启动"Activity",分别是:startActivity()、"startActivityForResult()",我们先来看第一个,关于启动"Activity"并且需要返回数据,我们会在后面谈论。

假设现在有一个"TestActivity",它由一个布局文件:activity_test,布局文件里面有个按钮,点击之后可以启动另外一个"Activity":SecondActivity,activity_test代码如下:

<?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">

<Button
android:id="@+id/btn_open_second_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="启动SecondActivity"
android:textSize="24sp"/>
</LinearLayout>

很简单的只有一个按钮而已。

接下来在"TestActivity"的"onCreate()"方法里面,我们先要找到控件(就是按钮),然后设置一个点击事件响应,通过"Intent"对象指定要启动的Activity,然后调用"startActivity()"方法即可,像这样子:

public class TestActivity extends Activity{

private Button openSecondActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
openSecondActivity = (Button)findViewById(R.id.btn_open_second_activity);//调用方法找到按钮
openSecondActivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(TestActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
}
这样就能启动出"SecondActivity",当然前提是你已经正确创建"SecondActivity"。

△启动"Activity"并且需要返回值

有时候当我们启动另外一个"Activity"是为了让他去完成某个工作,工作完成之后会返回原来的"Activity",而原来的"Activity"需要根据工作完成状况给予用户不同反馈信息,那么此时就需要被启动那个"Activity"能够返回某些数据回来,此时不能再使用"startActivity()"方法,我们需要"startActivityForResult()"方法。

假设现在有两个"Activity":TestActivity,SecondActivity,从“TestActivity"启动"SecondActivity","SecondActivity"里面有一个文本输入框(严格来讲是他的布局文件里有个文本输入框),如果用户在里面有输入任何数据,当"SecondActivity"返回时,告诉“TestActivity"用户已经完成输入,并且将数据送回去,在"TestActivity"里面通过Log打印出输入内容,如果用户什么都没输入,Log里面将会打印"什么东西都没输入“。

这里主要涉及几个方法调用,现在这里简介一下:

(1)onActivityResult:这个方法将在TestActivity里面重写,当我们从"SecondActivity"返回时,系统将会回调这个方法,不论用户有没输入什么东西,都会回调,我们在这个方法里利用标志进行判断,同时也在这里进行数据获取。

(2)startActivityForResult():与"startActivity()"方法类似,也是用来启动一个新的"Activity",不过这个方法告诉系统,当被启动的那一个"Activity"返回时,需要带回一些数据,其实就是告诉系统需要回调"onActivityResult()"方法,如果使用"startActivity()"方法启动新的活动,活动返回时将不会回调"onActivityResult()"方法,这个方法将在"TestActivity"里面重写。

(3)setResult():这个方法就是"SecondActivity()"用来返回数据用的,调用该方法可标识任务是否完成,同时可以携带任务所需要返回的数据。

(4)finish():这个是"Activity"用来结束自己用的方法。

我们先来看看"SecondActivity"布局文件里的代码,文件名字:activity_second,如下:

<?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">

<EditText
android:id="@+id/et_user_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请你输入一些东西"/>
<Button
android:id="@+id/btn_return"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="返回"/>
</LinearLayout>
一共只有两个控件,一个用来接收输入,一个用来返回到前一个活动。

现在看看逻辑代码:SecondActivity文件,如下:

public class SecondActivity extends Activity{

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Button returnButton = (Button)findViewById(R.id.btn_return);
final EditText userInput = (EditText)findViewById(R.id.et_user_input);
returnButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String userString = userInput.getText().toString();
// 如果用户有输入过什么东西
if(userString.equals("")){
setResult(RESULT_CANCELED);//其实这里不写也行,默认系统就会传入"RESULT_CANCELED"
finish();//关闭当前这个活动
}else {
// 到这说明用户什么都没输入
Intent data = new Intent();//Intent对象用来传递数据
data.putExtra("userInput", userString);
setResult(RESULT_OK, data);//注意这个必须调用,否则"TestActivity"会认为没有返回数据的
finish();//关闭当前这个活动
}
}
});
}
}
这里只设置了一个点击事件,当按下后,我们获取文本框的内容,然后判断是否有数据,如果没有任何文本,调用"setRdsult()“方法设置结果为"RESULT_CANCELED"标示的是任务取消,我们将没有数据当做取消了任务,当然即使你不调用,系统默认也会返回这个,这里只是为了提高可读性了。如果判断用户是有输入数据,就用Intent对象装着数据,同时设置"RESULT_OK"告诉系统任务已经处理完了,然后不论是否有输入过数据,我们都将"SecondActivity()"返回。


接下来看看"TestActivity"布局文件,文件名:activity_test,如下:

<?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">

<Button
android:id="@+id/btn_start_second_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="启动SecondActivity"/>
</LinearLayout>
代码也是非常简单,只有一个用来启动"SecondActivity"用的按钮。

好了接下来就到了核心代码,我们来看"TestActivity"里的代码,尤其需要关注的是"onActivityResult()"方法,像这样子:

public class TestActivity extends Activity {

private static int REQUEST_CODE = 0;
private static String TAG = "test";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
Button startSecondActivity = (Button)findViewById(R.id.btn_start_second_activity);
startSecondActivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(TestActivity.this,SecondActivity.class);
startActivityForResult(intent,REQUEST_CODE);
}
});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK){//判断"resultCode"看看任务是否完成
if(requestCode == REQUEST_CODE){//判断请求码是否能对上
Log.i(TAG,data.getStringExtra("userInput"));
}
}
if(resultCode == RESULT_CANCELED){//如果任务被取消了
Log.i(TAG,"什么东西都没输入");
return;
}
}
}
首先在"onCreate()"生命周期方法里面,我们为按钮设置了点击事件,用来启动新的活动,这里需要注意的,和前面的启动不同,如果使用"startActivity()"方法启动,返回之后"onActivityResult()"方法将得不到回调,使用"startActivityForResult()"就是需要告诉系统需要回调"onActivityResult()"方法。然后在"onActivityResult()"方法里面,我们需要进行判断,这里面有三个参数都很重要:

(1)首先,resultCode表示被启动的活动是否完成任务,如果值为"Result_OK"表示任务顺利完成,如果值为"RESULT_CANCELED"表示任务被取消了,当然这个返回值由被启动的活动决定。

(2)其次就是requestCode参数了,这个参数代表了请求码,试想一下如果在"TestActivity"活动里面可以启动多个不同活动,"onActivityResult()"方法被回调的时候,怎么知道是由哪个活动返回所导致的回调,只有通过请求码来区分。

(3)一个Intent对象,还记得么,"SecondActivity"里面我们是在一个Intent对象里面,设置了要返回的数据,现在这个对象就传到了这里,我们在这获取数据就可以了。

所有参数介绍完后,来看一下逻辑代码,如果任务顺利完成,再判断下请求码是否能对上,如果对上,就该执行我们的操作了,打印数据。另外一单任务取消,我们也会打印提示。

到此为止,关于如何启动一个活动同时要求返回数据,也基本都介绍完了。
 

△关闭"Activity"

系统提供这个方法关闭"Activity":finish(),哪个"Activity"调用了它就是关闭哪个"Activity"(就是活动用来自尽用的)。
另外需要补充的一点是,虽然你可以在程序里面手动关闭一个活动,不过,活动的生命应该由系统进行管理,最好不要人为去管理他,而且,被你手动关闭掉的活动,是不可以被回退的,就是不在回退栈里。当然如果像前面的例子那样,那就只能手动去关闭了。

△生命周期

先来一张生命周期全图:

 

 Activity及其生命周期小结

 

首先单独介绍一下各个生命周期方法:

→onCreate()

当且仅当Activity被创建的时候回调,从Java的角度去理解他,就是对象创建的时候来回调,你应当在这里对窗体做初始化,比如设置布局文件,以及其他一些初始化的设置,当然最重要的就是调用"setContentView()"方法设置布局文件。

→onStart()

当这个方法被回调,说明活动即将被用户所见了,注意了是即将可见,当该方法结束之后,用户就能够看见"Activity"了。

→onResume()

在调用该方法之前,用户已经能够看到“Activity”,在调用了这个方法之后,用户可以与"Activity"进行交互。

→onPause()

一旦调用了该方法,“Activity"即将失去用户焦点(比如临时弹出了一个对话框、或者当你在操作时突然来了一个电话,或者手机进入休眠,都会导致"Activity"失去用户的焦点),需要注意的一点是,如果仅仅回调了该方法,没有回调"onStop()"方法此时,此时用户还是能够看见"Activity",只是不能预知交互而已,只要用户离开活动,他是第一个会被回调的方法。你可以在该方法里保存一些较重要的用户信息,比如用户操作到一半时突然来了一个电话,接完这个电话之后他就不再继续操作,那他之前如果输入某些有用信息,必须保存。

→onStop()

当窗体不再为用户所见(比如有一个新窗体已经完全覆盖原来那个窗体,或者用户回到桌面,或者用户按开机键锁屏),只要你再看不见窗体了,这个方法就会回调。对于那些只是用来更新UI用的资源(比如广播的监听,监听数据变化然后更新UI,像这样的广播就能在该方法里面注销),可以在该方法里面释放,因为用户已经看不到活动了。

→onDestroy()

当活动被销毁的时候,就会回调这个方法,一旦该方法被回调,如无意外(比如内存泄漏就是一个意外),活动对象就会销毁。你需要在这个方法里面释放资源,比如你的活动启动了一个下载任务,你必须在这里释放与下载有关的所有资源(如果你的下载任务需要保持后台下载,那就不用释放这个资源)。

 →onRestart()

当活动从后台回到前台,系统就会回调这个生命周期方法,两个操作会导致活动从后台回到前台,一个就是你在某个活动上面直接按下“home”键,然后又重新回到该应用,这时就会回调这个方法。另一个是你在当前活动上面启动另外一个活动,然后又从新的活动返回旧的活动,旧的活动的该方法会被回调。一些跟UI更新相关的操作可能需要在该方法里面进行。

△几个操作所涉及的生命周期方法回调

我们重写所有生命周期方法,然后分别打印一下数据,然后执行几个操作,看看会有什么结果,就能感性体验一下"Activity“生命周期,代码很简单像这样:

public class TestActivity extends Activity {

private static String TAG = "test";
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
Log.i(TAG,"--onCreate--");
}

@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "--onStart--");
}

@Override
protected void onResume() {
super.onResume();
Log.i(TAG,"--onResume--");
}

@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "--onPause--");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG,"--onStop--");
}

@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG,"--onRestart--");
}

@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG,"--onDestroy--");
}
}
我们除了重写以及打印之外,什么事情都没有做。

点击屏幕启动活动:我们看到如下信息:

 Activity及其生命周期小结

一个活动完全运行起来,将会回调以下几个方法:onCreate()、onStart()、onResume()。

活动正常打开以后点回退键,我们看到如下信息:

 Activity及其生命周期小结

即按下了返回键后,又会回调几个方法:onPause()、onStop()、onDestroy()。

点击屏幕启动活动,这次不点击回退键,点击Home键:

 Activity及其生命周期小结

相比起点击回退键,少了一个onDestroy()周期。

让我们再回到最初始的时候,点击屏幕启动活动,这次不点击任何键,而是启动另外一个活动,我看到了如下信息

 Activity及其生命周期小结

可以看到这和启动“Activity"然后按下”home"键的情况是一样的。

然后我再从第二个"Activity"回去,我们看到如下信息:

 Activity及其生命周期小结

从第二个"Activity"返回,则原来的那个活动将会回调几个方法:onRestart()、onStart()、onResume()。

△生命周期图里面的几个循环

死记"Activity"生命周期方法没有很大意义,你注意到,生命周期图里面有几个循环,可以通过生命周期里的几个循环周期进行学习,同时,理解了这几个循环以后,你会明白我刚才的那些操作为什么会回调对应生命周期。

主要是有三个循环周期:

→entire lifetime(完整周期)

一个"Activity"的完整的生命周期,他是这样子的一个过程:onCreate()→onStart()→onResume()→onPause()→onStop()→onDestroy(),如何得到一个活动完整周期?点击屏幕打开一个活动,什么都不做然后就点返回键,就会回调上面几个生命周期方法,当你第二次再打开活动,又会重新调用onCreate()。可以认为,"onCreate()"方法以及"onDestroy()"方法就是一对的,通过处理这两方法里的代码,你就可以管理好"Activity"创建以及销毁时的任务。

visible lifetime(可见周期)

该周期内活动对于用户可见,他是这样子的一个过程:

onStart()→onResume()→onPause()→onStop()→onRestart()→onStart(),当被调用onStop(),活动便不再可见了。如果活动又重新回到屏幕上端,会从onRestart()开始回调。什么时候会发生这样的循环?当有一个活动正在运行,你去启动另外一个活动,再从新的活动里面退回,就会发生上述循环。可以认为"onStart()"方法以及"onStop()"是一对的,他们标志着活动对用户可见及不可见。处理好了这两个方法的代码,就能管理好活动在可见和不可见间的逻辑。

→foreground lifetime(前台周期)

处在这个周期里面,当前"Activity"处于其它所有的"Activity"上方,而且获取用户交互焦点。他是这样子的一个过程:onPause()→onResume()。从onResume()到onPause()可能会被频繁调用,因为,当设备进入了休眠状态,或者弹出一个会话,都会调用onPause()。类似的,可以当做"onResume()"方法以及"onPause()"是一对的,他们标志着焦点的得失。


△两个Activity间的生命周期交替

两个"Activity“间的生命周期交替,其实就是当一个"Activity"启动另外的一个"Activity"时,他们两个生命周期执行顺序。这个事情比较简单,这里直接说出结果。例如"Activity"A启动"Activity"D,那么两者生命周期执行顺序:A的"onPause()"→D的"onCreate()"→D的"onStart()"→D的"onResume()"→A的"onStop()"。提出这个过程做什么呢,主要为了以下两点:

(1)如果D里面有一些数据显示,是由A来提交的,比如D要显示数据库的数据,或者"SharedPreferences"文件里的某些数据,这些数据是由A提交的,那么A必须要在"onPause()"方法提交,D才能在他启动时所经历的生命周期里面读取数据,如果A在"onStop()"里才提交,D就读取不到数据。

(2)如果你需要在A离开时进行一些耗时操作,只能将操作放到"onStop()"方法里面,不要放在"onPause()"方法里面,因为如果"onPause()"方法一直没执行完,新的"Activity"跟本就启动不起来。当然最好的情况是,别在活动任何一个生命周期方法里面执行耗时操作。


△Activity数据保存与恢复

为什么要进行数据保存以及恢复

正常情况下结束掉"Activity",不用考虑状态保存与恢复的问题,对于意外被结束的"Activity",就要考虑这个问题。那么什么是意外被结束掉的"Activity"?最典型的一个例子就是屏幕方向变化。作为用户,如果我将屏幕方向横竖变化,对我而言,只是转了一个方向,界面上的所有东西(我已经输入的数据)应该原封不动地在哪里。很可惜的,对于安卓系统而言,如果屏幕发生旋转,他会将"Activity"重建。也就是说对于一个被旋转的"Activity",他会发生如下生命周期:onPause()→onStop()→onDestroy()→onCreate()→onStart()→onResume()。那么现在的问题是,如果我在活动上面按返回键,数据丢失是正常的,作为用户我能理解,但是如果我就只是转个屏幕,然后东西就都没了,这是我不能理解的。对于这么一个问题,就需要开发人员处理了。这就是意外结束的"Activity"需要进行状态保存以及恢复。
先来一张官网上的图片:
Activity及其生命周期小结

数据保存与恢复的过程及其关键方法

从图里面可以看到,数据保存与恢复的两个主要的负责人:onSaveInstanceState(Bundle)方法、onRestoreInstanceState(Bundle)方法(或者onCreate()方法)。正常情况下,"onSaveInstanceState()"方法以及"onRestoreInstanceState()"方法和活动的生命周期并无特别关联,不论怎么操作活动都不引发这两方法回调。但是当你对活动的操作导致活动意外结束,那么这两方法就会被回调了。从图里面可以看到这两方法被回调的时机,确切地说,如果"onSaveInstanceState()"会被回调,他将会在"onStop()"方法之前被回调,至于他和"onPause()"谁先回调,说不准的。而"onRestoreInstanceState()"方法,则在用户重新回到活动那是回调。
根据我们所描述的内容,以屏幕旋转为例子,状态保存及恢复的方法如下:重写"onSaveInstanceState()"方法,将数据保存到Bundle对象里面,然后重写"onRestoreInstanceState(Bundle)"方法,在方法里判断一下所传入的Bundle对象是否为空,如果不为空,说明你在"onSaveInstanceState()"方法里面曾经做过数据保存,只要取出数据然后恢复即可。让我们来看个例子体验一下。
Activity及其生命周期小结
我们放置两个控件:Button+TextView,每次点击按钮之后,记录他的点击次数,并显示到TextView那里。现在看看界面代码,界面名字:activity_test,如下:
<?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:id="@+id/tv_click_num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:gravity="center_horizontal"
android:text="你点击了0次按钮"/>

<Button
android:id="@+id/btn_be_click"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点我"/>
</LinearLayout>
就是两个控件而已。我们在活动"onCreate()"方法里面找到控件并且设置点击事件。如下:
    private TextView clickNumTextView;//这个是界面里面的TextView
private Button clickBtn;//这个是界面里面的Button
private int num;//这个成员变量用来保存点击次数

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 找到项对应的控件
clickNumTextView = (TextView)findViewById(R.id.tv_click_num);
clickBtn = (Button)findViewById(R.id.btn_be_click);
// 设置点击事件监听
clickBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 记录并且显示点击次数
num = num+1;
clickNumTextView.setText("你点击了"+num+"次按钮");
}
});
}

然后重写我们说的两个关键方法,首先"onSaveInstanceState()"如下:
    @Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("num",num);//通过Bundle对象保存数据
}
可以看到,我们使用方法里面传进来的"Bundle"对象进行数据保存,然后重写"onRestoreInstanceState()"方法进行数据恢复,如下:

    @Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 如果为空表示没有保存数据,直接返回
if(savedInstanceState==null){
return;
}
// 取出并且显示被保存的数据
clickNumTextView.setText("你点击了"+num+"次按钮");
}
现在来看整个活动完整代码:
public class MainActivity extends Activity {

private TextView clickNumTextView;//这个是界面里面的TextView
private Button clickBtn;//这个是界面里面的Button
private int num;//这个成员变量用来保存点击次数

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 找到项对应的控件
clickNumTextView = (TextView)findViewById(R.id.tv_click_num);
clickBtn = (Button)findViewById(R.id.btn_be_click);
// 设置点击事件监听
clickBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 记录并且显示点击次数
num = num+1;
clickNumTextView.setText("你点击了"+num+"次按钮");
}
});
}

// 将点击的次数保存到"Bundle"对象里面
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("num",num);//通过Bundle对象保存数据
}

// 取出被保存的数据
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 如果为空表示没有保存数据,直接返回
if(savedInstanceState==null){
return;
}
// 取出并且显示被保存的数据
clickNumTextView.setText("你点击了"+num+"次按钮");
}
}
这个例子到这就结束了,不过有个东西要说明下。我们说过这个是活动在意外情况下被结束时,用来进行数据保存及恢复的方法,但是这个绝对不是专门用来进行数据保存用的方式。如果你的活动不是意外结束,比如就是用户点了“返回”按键,这些方法根本不会被回调的。绝对不能依靠前述两个方法用来保存数据。对于那些很重要的必须进行保存用的数据(存在数据库或者是"SharedPreferences"文件里面存的数据),能依靠的就只有在"onPause()、:"onStop()"这类方法里面保存,不要完全去依赖"onSaveInstanceState()"方法或者"onRestoreInstanceState()"方法来做数据保存。这里需要强调的是,只有意外结束活动才能使用这个方式保存数据。至于有多少情况是“以外”情况,这个不在这里逐个列举,只要记住屏幕旋转是最典型,同时也是最容易拿来做测试用的情况。
关于活动数据保存以及恢复,也讲完了。