《安卓开发官方文档》学习笔记三activity生命周期
文章开始把我喜欢的这句话送个大家:这个世界上还有什么比自己写的代码运行在一亿人的电脑上更酷的事情吗,如果有那就是让这个数字再扩大十倍
指定程序首次启动的Activity
Activity只能在三种状态之一下存在很长时间。
- Resumed:在这种状态下,Activity处于前台,且用户可以与其交互。(有时也称为“运行”状态。)
- Paused:在这种状态下,Activity被在前台中处于半透明状态或者未覆盖整个屏幕的另一个Activity—部分阻挡。暂停的Activity不会接收用户输入并且无法执行任何代码。
- Stopped:在这种状态下,Activity被完全隐藏并且对用户不可见;它被视为处于后台。停止时,Activity实例及其诸如成员变量等所有状态信息将保留,但它无法执行任何代码。
其它状态(Created与Started)都是短暂的瞬态,系统会通过调用下一个生命周期回调方法从这些状态快速移到下一个状态。也就是说,在系统调用 onCreate()) 之后,它会快速调用 onStart()),紧接着快速调用 onResume())。
当用户从主界面点击程序图标时,系统会调用app中被声明为"launcher" (or "main") activity中的onCreate()方法。这个Activity被用来当作程序的主要进入点
main activity必须在manifest使用包括 MAIN
action 与 LAUNCHER
category 的<intent-filter>
标签来声明
<activity android:name=".MainActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
如果程序中没有声明了MAIN action 或者LAUNCHER category的activity,那么在设备的主界面列表里面不会呈现app图标。
我们必须实现onCreate()方法来执行程序启动所需要的基本逻辑,如声明UI元素,定义成员变量,配置UI等。(onCreate里面尽量少做事情,避免程序启动太久都看不到界面)
TextView mTextView;
// Member variable for text view in the layout @Override public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
/// The layout file is defined in the project res/layout/main_activity.xml filesetContentView(R.layout.main_activity);
// Initialize member TextView so we can manipulate it latermTextView = (TextView) findViewById(R.id.text_message);
// Make sure we're running on Honeycomb or higher to use ActionBar APIsif(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {// For the main activity, make sure the app icon in the action bar// does not behave as a button
ActionBar actionBar = getActionBar();
actionBar.setHomeButtonEnabled(
false);
} }
Caution:用SDK_INT来避免旧的系统调用了只在Android 2.0(API level 5)或者更新的系统可用的方法(上述if条件中的代码)。旧的系统调用了这些方法会抛出一个运行时异常。
销毁Activity
@Override
public void onDestroy(){
super.onDestroy(); // Always call the superclass // Stop method tracing that the activity started during onCreate()android.os.Debug.stopMethodTracing();
}
Note: 除非程序在onCreate()方法里面就调用了finish()方法,系统通常是在执行了onPause()与onStop() 之后再调用onDestroy() 。在某些情况下,例如我们的activity只是做了一个临时的逻辑跳转的功能,它只是用来决定跳转到哪一个activity,这样的话,需要在onCreate里面调用finish方法,这样系统会直接调用onDestory,跳过生命周期中的其他方法。
大多数app并不需要实现这个方法,因为局部类的references会随着activity的销毁而销毁,并且我们的activity应该在onPause()与onStop()中执行清除activity资源的操作。然而,如果activity含有在onCreate调用时创建的后台线程,或者是其他有可能导致内存泄漏的资源,则应该在OnDestroy()时进行资源清理,杀死后台线程
通常,不应该使用onPause()来保存用户改变的数据(例如填入表格中的个人信息) 到永久存储(File或者DB)上。仅仅当确认用户期待那些改变能够被自动保存的时候(例如正在撰写邮件草稿),才把那些数据存到永久存储。但是,我们应该避免在onPause()时执行CPU-intensive的工作,例如写数据到DB,因为它会导致切换到下一个activity变得缓慢(应该把那些heavy-load的工作放到onStop()去做)。
如果activity实际上是要被Stop,那么我们应该为了切换的顺畅而减少在OnPause()方法里面的工作量。
Note:当activity处于暂停状态,Activity实例是驻留在内存中的,并且在activity恢复的时候重新调用。我们不需要在恢复到Resumed状态的一系列回调方法中重新初始化组件。
恢复activity
onResume()来初始化那些在onPause方法里面释放掉的组件,并执行那些activity每次进入Resumed state都需要的初始化动作
停止与重启Activity
Note: 因为系统在activity停止时会在内存中保存Activity的实例,所以有时不需要实现onStop(),onRestart()甚至是onStart()方法. 因为大多数的activity相对比较简单,activity会自己停止与重启,我们只需要使用onPause()来停止正在运行的动作并断开系统资源链接。
一旦activity停止了,系统会在需要内存空间时摧毁它的实例(和栈结构有关,通常back操作会导致前一个activity被销毁)。极端情况下,系统会直接杀死我们的app进程,并不执行activity的onDestroy()回调方法, 因此我们需要使用onStop()来释放资源,从而避免内存泄漏。(这点需要注意)
尽管onPause()方法是在onStop()之前调用,我们应该使用onStop()来执行那些CPU intensive的shut-down操作,例如往数据库写信息
保存View对象的状态(比如EditText中的文字) 到一个Bundle中,Activity对象会保存在内存中
应该使用onStart()作为onStop()所对应方法
我们在onStop里面做了哪些清除的操作,就该在onStart里面重新把那些清除掉的资源重新创建出来。
我们会在onStop方法里面做释放资源的操作,那么onDestory方法则是我们最后去清除那些可能导致内存泄漏的地方
当Activity是因为用户点击Back按钮或者是activity通过调用finish()结束自己时,系统就丢失了对Activity实例的引用,因为这一行为意味着不再需要这个activity了。然而,如果因为系统资源紧张而导致Activity的Destory,系统会在用户回到这个Activity时有这个Activity存在过的记录,系统会使用那些保存的记录数据(描述了当Activity被Destory时的状态)来重新创建一个新的Activity实例。那些被系统用来恢复之前状态而保存的数据被叫做"instance state" ,它是一些存放在Bundle对象中的key-value pairs
Caution: 你的Activity会在每次旋转屏幕时被destroyed与recreated。当屏幕改变方向时,系统会Destory与Recreate前台的activity,因为屏幕配置被改变,你的Activity可能需要加载另一些替代的资源(例如layout).
Note: 为了使Android系统能够恢复Activity中的View的状态,每个View都必须有一个唯一ID,由android:id定义。
为了可以保存额外更多的数据到saved instance state。在Activity的生命周期里面存在一个额外的回调函数,你必须重写这个函数。该回调函数并没有在前面课程的图片示例中显示。这个方法是onSaveInstanceState(),当用户离开Activity时,系统会调用它。若系统在Activity被Destory之后想重新创建这个Activity实例时,之前的Bundle对象会(系统)被传递到你我们activity的onRestoreInstanceState()方法与onCreate() 方法中。
(跳转到其他的activity或者是点击Home都会导致当前的activity执行onSaveInstanceState,因为这种情况下的activity都是有可能会被destory并且是需要保存状态以便后续恢复使用的,而从跳转的activity点击back回到前一个activity,那么跳转前的activity是执行退栈的操作,所以这种情况下是不会执行onSaveInstanceState的,因为这个activity不可能存在需要重建的操作)
static final String STATE_SCORE = "playerScore"; static final String STATE_LEVEL="playerLevel"; @Override public void onSaveInstanceState(Bundle savedInstanceState){
// Save the user's current game statesavedInstanceState.putInt(STATE_SCORE, mCurrentScore); savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy statesuper.onSaveInstanceState(savedInstanceState);
}
为了给Activity保存额外的状态信息,你必须实现onSaveInstanceState() 并增加key-value pairs到Bundle 对象中
Caution: 必须要调用onSaveInstanceState() 方法的父类实现,这样默认的父类实现才能保存视图状态的信息。
由于onCreate() 方法会在第一次创建新的Activity实例与重新创建之前被Destory的实例时都被调用,我们必须在尝试读取Bundle 对象前检测它是否为null。如果它为null,系统则是创建一个新的Activity实例,而不是恢复之前被Destory的Activity。
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState); // Always call the superclass first// Check whether we're recreating a previously destroyed instanceif(savedInstanceState !=null){
// Restore value of members from saved statemCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
else{
// Probably initialize members with default values for a new instance}}
我们也可以选择实现onRestoreInstanceState() ,而不是在onCreate方法里面恢复数据。 onRestoreInstanceState()方法会在onStart() 方法之后执行. 系统仅仅会在存在需要恢复的状态信息时才会调用onRestoreInstanceState() ,因此不需要检查Bundle 是否为null。
public void onRestoreInstanceState(Bundle savedInstanceState){
// Always call the superclass so it can restore the view hierarchysuper.onRestoreInstanceState(savedInstanceState);mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}