Android官方开发教程中文版(四)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Android官方开发教程中文版
管理Activity的生存周期
管理Activity的生存周期
当用户通过导航离开然后又返回你的App,App的Activity实例在它们的生存周期内在两个不同的状态之间转换。
例如,Activity首次启动时成为系统的前台并接受用户的关注,在这个过程中,Android系统在你的Activity上调用一系列生命周期方法设置用户界面和其它组件。
如果用户执行了一个启动另一个Activity或切换到其它App的动作,系统调用生命周期的其它方法把Activity移到后台(Activity不再可见,但实例和状态仍然完好无损)。
在生命周期的回调方法中,你可以定义当用户离开和重新进入Activity时,Activity的行为。
例如,如果你正在创建一个流媒体视频播放器,当用户切换到其它App时,你可以暂停视频并中断网络连接。
当用户返回时,你再重新连接网络并允许用户从暂停点恢复播放。
本课程解释了重要的生命周期回调方法,每个Activity实例接收和如何使用它们,使Activity执行用户期望的结果,并且当Activity不需要它们时,不会消耗系统资源。
启动Activity
不像其它编程范例由main()方法启动,Android系统在Activity中通过调用对应生命周期特定阶段的回调方法来启动代码。
在启动Activity时有一系列的回调方法,销毁Activity时也有一系列的回调方法。
本节内容提供了大部分重要的生命周期方法的概览,并且展示了在创建Activity的新实例时如何处理首个生命周期的回调。
理解生命周期回调
在Activity的生存期内,系统在一个阶梯状金字塔的序列中调用核心的生命周期方法。
也就是说,Activity生命周期的每一个阶段都是这个金字塔上的一个台阶。
随着系统创建新的Activity实例,每个回调方法都把Activity的状态向顶部移动一步,当到达顶部时,Activity 运行在前台并可以和用户交互。
当用户开始离开Activity时,系统调用其它的回调方法把Activity的状态返回到金字塔底部以消除Activity。
在某些情况下,Activity仅仅移动到金字塔的中间并等待(例如用户切换到其它App),Activity可以从这里回到顶部(如果用户返回了Activity)并且从用户离开的地方恢复。
根据Activity 的复杂度,你可能不需要实现所有的生命周期方法。
不管怎样,重要的是你要理解每个回调方法并能实现它们,以确保你的App 表现得如用户期望的那样。
正确地实现Activity 的生命周期方法以确保你的App 在以下几个方面表现良好,包括:
● 用户在使用你的App 时接到电话或切换到其它App 时,不能崩溃。
● 用户不再和App 频繁交互时,不能浪费宝贵的系统资源。
● 当用户离开App 并在稍后返回时,不能丢失用户的进程。
● 当屏幕在水平和垂直方向间切换时,用户的进程不能丢失或崩溃。
在接下的课程中你将学习到,Activity 在图一所示的不同状态间转换的几种情况。
任何情况下,这些状态中只有三个会静止不变,也就是说,同一时间只能存在以下三个状态中的一个:
Resumed
这个状态下,Activity 运行在前台,并且用户可以和它交互(有时也被称为“Running ”状态)。
Paused
这个状态下,Activity 被另一个Activity 部分遮盖——另一个Activity 运行在前台,并且背景是半透明的或者没有覆盖整个屏幕。
暂停的Activity 不会接收用户输入,也不会执行任何代码。
Stopped
这个状态下,Activity 完全隐藏并对用户不可见,它被认为处于后台。
当停止时,Activity 实例以及它所有的状态信息,如成员变量仍然保持,但不会执行任何代码。
其它状态(Create 和Started )非常短暂,系统通过调用下一个生命周期方法,很快从它们移动到下一个状态。
也就是说,系统调用onCreate()之后,很快调用onStart(),接着又立即调用onResume()。
这就是Activity 生命周期的基本知识,现在你要开始学习某些特定的生命周期行为。
指定你的App 的启动Activity
当用户从Home 屏幕上选择你的App 图标时,系统在App 内为你声明的“执行”(或“主”)Activity 调用onCreate()方法。
这是你的App 用户界面的主入口点。
图一:用阶梯状金字塔图解Activity 生命周期。
展示了每个回调方法是如何使Activity 向顶部的Resume 状态靠近,还有把Activity 带回到底部的回调方法。
Activity 也可以从Paused 和Stopped 状态回到Resumed 状态。
你可以在Android清单文件中声明哪个Activity成为你的主Activity,AndroidManifest.xml 文件在你的项目根目录中。
App的主Activity必须在清单文件的<intent-filter>中定义,包含值为MAIN的action和值为LAUNCHAR的category属性,如:
<activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="UNCHER" />
</intent-filter>
</activity>
注意:当你使用Android SDK工具创建Android项目时,默认的项目文件就包括一个用这个过滤器(filter)声明在清单文件中的Activity类。
如果你所有的Activity都没有包含值为MAIN的action和值为LAUNCHAR的category属性,那么你的App图标不会出现在Home屏幕的App列表中。
创建新实例
大多数App都包含了几个Activity以允许执行不同的操作。
无论是由用户点击App图标时创建的主Activity,还是你的App在响应用户操作时启动的Activity,系统都会调用onCreate()方法创建Activity的实例。
你必须实现onCreate()方法来执行基本的应用程序启动逻辑,这些逻辑在Activity的整个生存期内只会发生一次。
比如,你的onCreate()方法应该定义用户界面和实例化某些类变量。
下例演示了为Activity执行某些基本设置的部分代码,如定义用户界面(在XML布局文件中声明),声明成员变量以及配置某些UI。
TextView mTextView; // 布局中文本视图的成员变量
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//为当前Activity设置用户界面
// 布局文件定义在项目的res/layout/main_activity.xml 文件中
setContentView(yout.main_activity);
// 初始化TextView成员,这样我们可以在后面操作它
mTextView = (TextView) findViewById(R.id.text_message);
// 确保我们运行在Honeycomb或更高版本下,以便使用操作栏API
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// 对主Activity,确保App的图标在操作栏中
// 不要看起来象个按纽
ActionBar actionBar = getActionBar();
actionBar.setHomeButtonEnabled(false);
}
}
警告:用SDK_INT这种方式来防止旧系统执行新的API仅在Android2.0(API级别5)或更高版本下有效,旧版本这样做将会产生一个运行时异常。
一旦onCreate()执行完毕,系统会紧接着调用onStart()和onResume()方法,你的Activity 永远都不会停留在Created或Started状态。
从技术上说,当调用onStart()时,Activity就对用户可见,但onResume()会紧跟着被调用,Activity将保持在Resume状态直到某些事情改变它。
比如收到一个电话,用户导航到其它Activity,或者设备屏幕关闭。
在接下来的课程中,你将看到其它启动方法:onStart()和onResume()在Activity从Paused 和Stopped状态中恢复时是如何使用的。
注意:onCreate()方法有一个参数savedInstanceState,我们将在稍后的课程“重建Activity”中讨论它。
销毁Activity
Activity的第一个生命周期回调方法是onCreate(),最后一个生命周期回调方法是onDestroy()。
系统调用onDestroy()方法标识着你的Activity从系统内存中完全移除。
大部分App不需要实现这个方法,因为本地类的引用会随Activity一起被销毁,你的Activity应该在onPause()和onStop()中执行大部分的清理。
无论如何,如果你的Activity包含了在onCreate()中创建的后台线程或其它长期资源,没有正确关闭的话可能会造成内存泄漏,你应该在onDestroy()中终止它们。
@Override
public void onDestroy() {
super.onDestroy(); // 首先调用基类方法
//停止在onCreate()中开始的对方法的跟踪
android.os.Debug.stopMethodTracing();
}
注意:系统会在任何情况下调用onPause()和onStop()方法后调用onDestroy(),只有一种情况除外:在onCreate()方法内调用了finish()方法。
在某些情况下,例如你的Activity临时决定启动另一个Activity,你可以从onCreate()方法内调用finish()以销毁Activity。
在这个案例中,系统会在不调用其它任何生命周期方法的情况下立即调用onDestroy()方法。
暂停及恢复Activity
在App正常使用的情况下,有时候前台Activity会被其它可视组件遮挡从而导致Activity 暂停。
比如一个半透明的Activity(如对话框风格之类的)打开时,之前的Activity会暂停。
虽然这个Activity有部分可见,但它当前没有拥有焦点,所以保持在暂停状态。
不管怎样,一旦Activity被全部遮挡而不可见时,它将停止(我们在下一节中讨论)。
Activity进入暂停状态时,系统会调用onPause()方法,以允许你停止正在进行的行为,暂停点(如视频)或任何存留的信息都应该被持久保存以防止用户继续离开你的App。
如果用户从暂停状态返回到Activity,系统恢复它并调用onResume()方法。
注意:当Activity收到onPause()的调用,它可能标识着你的Activity将会暂停,然后用户可能会把焦点返回到你的Activity。
不管怎样,这通常都是用户离开App的第一个迹象。
暂停Activity
当系统为你的Activity调用onPause()时,技术上意味着你的Activity仍然有部分可见。
但更多情况下表示用户正在离开这个Activity,并且Activity将很快进入停止状态。
你应该在onPause()回调方法中做到:
●停止动画或其它消耗CPU的行为。
●提交未保存的改变,但只有当用户希望这个改变被持久保存时才这么做(比如一个
Email的草稿)。
●当Activity暂停并且用户不再需要它们时,释放系统资源。
比如广播接收器、对传
感器的操作(GPS之类)或者任何影响电池寿命的资源。
例如,你的应用程序使用了照相机,onPause()方法是释放它的好地方:
@Override
当一个半透明Activity遮挡你的Activity时,系统调用onPause()方法并且Activity在暂停状态等待(1),如果用户在仍然暂停时返回Activity,系统将调用onResume()(2)。
public void onPause() {
super.onPause(); //首先调用基类方法
// 释放照相机,因为暂停时我们不需要它
// 而其它Activity可能会使用它
if (mCamera != null) {
mCamera.release()
mCamera = null;
}
}
通常情况下,你不应该在onPause()方法中把用户的改变持久保存(比如登录到表单中的个人信息)。
只有你确信用户希望这些改变会自动保存时(比如Email草稿),才在onPause()方法中把用户的改变持久储存。
任何情况下,你都应该避免在onPause()中执行增加CPU负担的工作,比如写入数据库,因为这可能会使过渡到下一个Activity变得迟缓(你应该在onStop()的关闭操作中执行繁重的工作)。
如果你的Activity被停止了,你应该在onPause()方法中保持操作完成的数量,以允许快速过渡到用户的下一个目标。
注意:当Activity暂停时,Activity实例仍然驻留在内存中并在恢复时重新使用,你不需要重新初始化在Resume状态之前被回调方法创建的组件。
恢复Activity
当Activity从暂停状态中恢复时,系统会调用onResume()方法。
请注意,每当你的Activity进入前台,系统就会调用这个方法,包括它首次被创建时。
这样一来,你应该实现onResume()方法来初始化在onPause()方法中释放的组件,以及执行其它在Activity每次进入Resume状态时必须发生的初始化工作(比如开始动画以及初始化仅在用户拥有焦点时用到的组件)。
下面onResume()的示例对应上面的onPause()示例,它初始化了在Activity暂停时释放的照相机。
@Override
public void onResume() {
super.onResume(); //首先调用基类方法
// 当Activity获得用户焦点时得到照相机的实例
if (mCamera == null) {
initializeCamera(); // 对照相机初始化的本地方法
}
}
停止和重新启动Activity
在Activity生存周期内正确地停止和重启Activity是一个重要的步骤,它可以确保用户意识到App一直存活并且他们的进度没有丢失。
以下几个关键场景中,Activity会停止和重启:
用户打开一个新的App窗口,以及从你的App切换到另一个App,你的App中当前处于前台的Activity被停止。
如果用户在“Home”屏幕中执行App图标或从新
App窗口中返回到你的App,Activity重新启动。
●用户在你的App中执行某个操作启动一个新Activity,当第二个Activity被创建时,
当前Activity停止。
如果用户按下“Back”按纽,第一个Activity重新启动。
●用户在手机上使用App时,接到电话。
Activity类提供了两个生命周期方法:onStop和onRestart(),它们允许你专门处理Activity 的停止和重新启动。
不像暂停状态只有部分UI被遮挡,停止状态确保UI不可见,并且用户的焦点处于不同的Activity(或完全不同的App)。
注意:因为Acctivity停止时,系统仍然会把它保留在内存中,你也许不需要在任何情况下都实现onStop(),onRestart()(或onStart())方法。
对大部分Activity来说,简单地停止并重启就行了,你只需要在onPause()中暂停正在进行的操作以及断开和系统资源的连接。
当用户离开Activity时,系统调用onStop()停止Activity(1),如果用户从停止中返回,
系统调用onRestart()(2),紧接着调用onStart()(3)和onResume()(4)。
注意,不管什么
情况导致Activity停止,系统都会在调用onStop()之前调用onPause()。
停止Activity
当Activity收到onStop()方法的调用时,它变得不可见并且释放几乎所有不再使用的资源。
Activity一旦停止,如果需要释放内存,系统可能会销毁它的实例。
在极端情况下,系统可能简单地杀掉App的进程而不调用onDestroy()。
因此,在可能存在内存泄漏的情况下,使用onStop()释放资源非常重要。
虽然onPause()在onStop()之前调用,你应该在onStop()中执行那行更大、更占CPU资源的关闭操作,例如把信息写入数据库。
下面是一个在onStop()中把便笺的草稿内容持久化保存的例子:
@Override
protected void onStop() {
super.onStop(); //首先调用基类方法
// 保存便笺当前的草稿,因为Activity正在停止
// 我们不希望当前便笺的进度被丢失
ContentValues values = new ContentValues();
values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());
getContentResolver().update(
mUri, // 要更新的便笺的URI
values, // 列名映射以及应用到上面的新值
null, // 未使用SELECT参数
null // 未使用WHERE参数
);
}
当Activity停止时,Activity实例仍然驻留在内存中并在恢复时重新使用,你不需要重新初始化在Resume状态之前被回调方法创建的组件。
系统会对布局中的每个视图保持跟踪,如果用户在一个EditText部件中输入了文本,文本将会被保留,所以你不需要保存和恢复它。
注意:即使系统在Activity停止时把它销毁,视图对象(如EditText中的文本)的状态仍然保留在Bundle(键值对中的一个)中,并且在用户返回到Activity同一个实例时恢复它们(下一节讨论更多关于使用Bundle保存其它状态数据以防止Activity被销毁和重建的内容)。
启动/重新启动Activity
Activity从停止状态返回到前台时会调用onRestart()。
每当Activity成为可见时,系统都会调用onStart()方法(whether being restarted or created for the first time),只有Activity从停止状态中恢复时才会调用onRestart()方法,如果Activity只是被停止而没有被销毁,你可能有必要在onRestart()方法中执行一些特殊的恢复工作。
App使用onRestart()恢复Activity状态的情况很少见,因此对这种应用方式没有任何指南。
因为在onStop()方法中清除了Activity的所有资源,所以你需要在Activity重新启动时重新初始化它们。
另外,Activity首次创建时,你也需要实例化这些资源(当它们在Activity实例中还不存在时)。
基于此,你应该用onStart()方法来对应onStop()方法,因为系统在创建Activity和从停止状态中恢复Activity时都会调用onStart()方法。
例如,在返回App之前,用户可能已经离开很长时间了,onStart()方法是一个用来验证所需的系统功能是否还允许的好地方。
@Override
protected void onStart() {
super.onStart(); //首先调用基类方法
// Activity可能是首次启动或重新启动
// 我们应该确保GPS功能已经启用
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
boolean gpsEnabled = locationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!gpsEnabled) {
// 创建一个对话框要求用户启用GPS,当用户点击“OK”时,
// 用android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS操作
// 打开设置屏幕,以便用户启用GPS。
}
}
@Override
protected void onRestart() {
super.onRestart(); //首先调用基类方法
// Activity从停止状态中恢复
}
重建Ativity
某些情况下,Activity被销毁是App的正常行为。
比如用户按下“Back”按纽,或是Activity 明确调用finish()方法来摧毁自己。
如果Activity处于停止中,并且用户很长时间都没有使用它,或是前台Activity需要更多系统资源,系统不得不关闭后台进程以恢复内存,系统也可能会销毁Activity。
当Activity因为用户按下“Back”按纽或通过finish()方法结束自己时,由于这些行为表示Activity不再需要,系统会认为Activity实例永久消失。
如果Activity被系统强制(相对于App的正常行为)销毁,虽然Activity实例已经消亡,但系统会“记得”它曾经存在,如果用户导航回来,系统会使用销毁时保存的数据和状态描述来创建新的Activity。
系统用来恢复之前的状态(被称为“实例状态”)的这些数据保存在Bundle对象的键值对集合中。
警告:用户每次旋转屏幕时,Activity都会被销毁并重新创建。
当屏幕方向改变时,系统销毁并重建前台Activity,因为屏幕设置发生变化,而且Activity可能要加载可替换资源(如布局)。
默认情况下,系统使用Bundle实例状态保存Activity中每个视图对象的信息(如EditText 对象中的文本值)。
所以当Activity被销毁和重建时,不需要通过代码来恢复布局状态。
Activity 可以有更多你想恢复的状态信息,比如一个跟踪用户进度的成员变量。
注意:要让Android系统恢复Activity中的视图状态,每个视图必须有一个唯一ID,由android:id属性提供。
要保存有关Activity状态的额外数据,你必须重写onSaveInstaneState()回调方法。
当用户离开Activity时,系统调用这个方法并传递Bundle对象,在Activity被意外摧毁时保存Bundle对象。
如果系统必须在稍后重建Activity实例,它会把相同的Bundle对象传递给onRestoreInstance()和onCreate()方法。
系统开始停止Activity时,它调用onSaveInstance()(1),你可以在这里指定Activity重建时需要恢
复的额外的状态数据。
如果Activity被摧毁并要重建相同实例,系统会把(1)中定义的状态数据
传递给onCreate()(2)和onRestoreInstance()(3)方法。
保存Activity状态
当Activity开始停止时,系统调用onSaveInstance()方法,Activity可以使用键值对集合保存状态信息。
这个方法的默认实现会保存Activity视图层次的状态信息,比如EditText的文本或ListView的滚动条位置。
要保存Activity的额外状态信息,你必须实现onSaveInstance()方法并向Bundle对象中添中键值对,例如:
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// 保存用户当前的游戏状态
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// 总是调用基类方法,这样它可以保存视图层次状态
super.onSaveInstanceState(savedInstanceState);
}
警告:永远要调用onSaveInstance()的基类实现,默认的实现会保存视图层次的状态。
恢复Activity状态
当Activity在销毁后重建时,你可以从系统传递给Activity的Bundle中恢复保存的状态。
onCreate()和onRestoreInstance()方法都会收到相同的Bundle对象,它包含实例状态信息。
因为系统创建Activity的新实例或是重建之前的Activity都会调用onCreate()方法,所以你在尝试读取Bundle对象之前必须检查它是否为null。
如果为null,系统在创建新的实例,否则就是恢复之前被摧毁的Activity。
下例是如何在onCreate()中恢复某些状态数据:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // 首先调用基类
// 检查是否在重建之前摧毁的实例
if (savedInstanceState != null) {
// 从保存的状态中恢复成员的值
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// 可能需要为新实例初始化成员的默认值
}
...
}
你也可以选择实现onRestoreInstance()来替换onCreate()以恢复状态,系统会在onStart()
方法后调用它。
系统仅在恢复保存的状态时才会调用onRestoreInstance()方法,所以你不需要检查Bundle对象是否为空。
public void onRestoreInstanceState(Bundle savedInstanceState) {
// 总是调用基类方法,它能恢复视图层次
super.onRestoreInstanceState(savedInstanceState);
// 从保存的实例中恢复状态成员
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
警告:永远要调用onRestoreInstance()的基类实现,默认的实现会恢复视图层次的状态。
学习更多有关在运行时重新启动事件中重建Activity的内容(如屏幕旋转),请参考“处理运行时的改变”。