Android Activity的一些要點
Activity是安卓四大組件之一,也是使用最頻繁的組件,這里就來記錄下在使用Activity的過程中應該注意的一些小細節
一、Activity的生命周期
- "onStart與onResume"、"onPause與onStop"在描述上來看都是類似的,當Activity開始創建調用了onCreate方法后,會依次調用"onStart與onResume",當Activity切換時到另一個界面時,會依次調用"onPause與onStop"
那么,它們之間有什么不同呢?可不可以只保留其中之一呢?
其實,onStart與onResume的差異在于要啟動的Activity是否可見,兩者都是Activity從創建到顯示在前臺這之間被調用的,不過onStart方法被調用時Activity還在后臺,onResume被調用時表示Activity已經可見并開始活動了
onPause與onStop在用戶切換Activity或者打開一個Dialog時調用。當用戶直接切換到一個非透明Activity時,onPause與onStop會依次調用,如果下一個Activity是透明主題,或者打開的是一個Dialog(只遮擋了一部分當前Activity的界面)時,只會調用onPause,而不會調用onResume - 當前臺Activity A切換到Activity B時,兩者之間的回調函數調用順序是怎樣的呢?
首先新建一個MainActivity,重寫其各個回調函數,添加一句Log輸出語句,類似如下所示private final String TAG = "MainActivity"; @Override protected void onStart() { super.onStart(); Log.e(TAG, "onStart被調用"); }
也重寫Main2Activity的各大回調函數,與MainActivity類似private final String TAG = "第二個Activity"; @Override protected void onStart() { super.onStart(); Log.e(TAG, "onStart被調用"); }
com.czy.myapplication E/MainActivity: onCreate被調用 com.czy.myapplication E/MainActivity: onStart被調用 com.czy.myapplication E/MainActivity: onResume被調用
com.czy.myapplication E/MainActivity: onPause被調用 com.czy.myapplication E/第二個Activity: onCreate被調用 com.czy.myapplication E/第二個Activity: onStart被調用 com.czy.myapplication E/第二個Activity: onResume被調用 com.czy.myapplication E/MainActivity: onStop被調用
com.czy.myapplication E/第二個Activity: onPause被調用 com.czy.myapplication E/MainActivity: onRestart被調用 com.czy.myapplication E/MainActivity: onStart被調用 com.czy.myapplication E/MainActivity: onResume被調用 com.czy.myapplication E/第二個Activity: onStop被調用 com.czy.myapplication E/第二個Activity: onDestroy被調用
因此,原Activity的onPause不能用于執行耗時操作,否則會影響新的Activity的顯示
二、Activity異常情況下的數據保存
-
當Android系統資源相關的配置發生變化(如橫豎屏切換)時,默認情況下當前Activity就會被銷毀并重新創建
此時Activity是由于異常情況而銷毀的,所以系統會調用如下方法來保存當前數據
protected void onSaveInstanceState(Bundle outState)
即我們可以向Bundle中存入需要恢復的數據
當Activity被重新創建后,以下方法會被自動調用
protected void onRestoreInstanceState(Bundle savedInstanceState)
該Bundle就是在onSaveInstanceState中所使用的,我們可以從中取出數據,用于恢復Activity的狀態
此外,該Bundle同時也會傳遞給
protected void onCreate(Bundle savedInstanceState)
因此我們可以選擇在兩個方法其中之一來恢復數據,一般是在onRestoreInstanceState(Bundle savedInstanceState)當中執行恢復操作比較合理
且onRestoreInstanceState方法只在Bundle不為空時才會調用,而在onCreate方法中還需要再次判斷Bundle的值
例如,如果當前Activity中有一個EditText,當屏幕發生旋轉時,可以將其文本內容存入Bundle中,當重新創建Activity時再將其從Bundle取出并顯示在EditText當中
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
EditText editText = (EditText) findViewById(R.id.editText);
if (!TextUtils.isEmpty(editText.getText())) {
outState.putString("TextContent", editText.getText().toString());
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String content = savedInstanceState.getString("TextContent", "");
EditText editText = (EditText) findViewById(R.id.editText);
editText.setText(content);
}
View本身也包含有該兩個方法,某些View的數據保存與恢復操作系統已經自動為我們完成了,這也是我們旋轉屏幕重新創建Activity后某些狀態依然一樣的原因
從Activity第一次創建到旋轉屏幕,各回調函數的執行順序如下所示:
com.czy.myapplication E/MainActivity: onCreate被調用
com.czy.myapplication E/MainActivity: onStart被調用
com.czy.myapplication E/MainActivity: onResume被調用
com.czy.myapplication E/MainActivity: onPause被調用
com.czy.myapplication E/MainActivity: onSaveInstanceState被調用
com.czy.myapplication E/MainActivity: onStop被調用
com.czy.myapplication E/MainActivity: onDestroy被調用
com.czy.myapplication E/MainActivity: onCreate被調用
com.czy.myapplication E/MainActivity: onStart被調用
com.czy.myapplication E/MainActivity: onRestoreInstanceState被調用
com.czy.myapplication E/MainActivity: onResume被調用
2 . 禁止因系統配置改變而重新創建Activity
如果不想在系統配置被改變后而導致Activity重新創建,可以在Activity聲明時為之指定configChanges屬性
android:configChanges="orientation|screenSize"
這樣,當屏幕旋轉時Activity就不會重新創建也不會去進行數據保存和數據恢復操作了,此時系統會改為去調用以下方法
public void onConfigurationChanged(Configuration newConfig)
重寫之
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.e(TAG, "onConfigurationChanged被調用,屏幕方向:" + newConfig.orientation);
}
這樣,當多次旋轉屏幕時,onSaveInstanceState和onRestoreInstanceState均不會被調用,調用的只是onConfigurationChanged方法,且Activity并沒有銷毀重建
com.czy.myapplication E/MainActivity: onCreate被調用
com.czy.myapplication E/MainActivity: onStart被調用
com.czy.myapplication E/MainActivity: onResume被調用
com.czy.myapplication E/MainActivity: onConfigurationChanged被調用,屏幕方向:2
com.czy.myapplication E/MainActivity: onConfigurationChanged被調用,屏幕方向:1
com.czy.myapplication E/MainActivity: onConfigurationChanged被調用,屏幕方向:2
com.czy.myapplication E/MainActivity: onConfigurationChanged被調用,屏幕方向:1
三、Activity的啟動模式
Android共有四種Activity啟動模式,分別為:standard、singleTop、singleTask、singleInstance
Activity位于任務棧當中,棧是一種“后進先出”的結構,當點擊back鍵時,棧頂的Activity會一一出棧,直到棧為空后退出程序
Activity的默認棧名為包名,可以通過taskAffinity屬性來設置,該屬性必須包含有包名分隔符“.”,而不能僅僅是一個單詞
<activity
android:name=".AnotherActivity"
android:launchMode="singleTask"
android:taskAffinity="com.custom.task" />
taskAffinity屬性主要和singleTask模式和allowTaskReparenting屬性配合使用,在其他情況下沒有作用
(1) standard
此為標準模式,即Activity的默認啟動模式
在該啟動模式下,每啟動一個Activity,不管該Activity是否已存在,均會創建一個新的Activity實例,并將該Activity壓入啟動它的Acivity所在的任務棧的棧頂(singleInstance模式除外),不管該棧的棧名和新啟動的Activity在AndroidManifest中指定的taskAffinity屬性是否相同
(2) singleTop
此為棧頂復用模式
如果要啟動的Activity已位于任務棧的棧頂,則此Activity不會被重新創建
不過,此時該Activity的以下方法會被調用
protected void onNewIntent(Intent intent)
可以通過該intent來傳輸數據
例如,在啟動Activity時,向intent傳入文本信息
public void startAnotherActivity(View view) {
Intent intent = new Intent(this, AnotherActivity.class);
Bundle bundle = new Bundle();
bundle.putString("Content", "要傳輸的內容");
intent.putExtras(bundle);
startActivity(intent);
}
然后在onNewIntent方法中再來獲取數據
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.e(TAG, "onNewIntent被調用");
Bundle bundle = intent.getExtras();
if (bundle != null) {
String text = bundle.getString("Content", "默認文本");
Toast.makeText(AnotherActivity.this, text, Toast.LENGTH_SHORT).show();
}
}
需要注意的是:如果要啟動的Activity并不位于棧頂,則會創建該Activity實例,且onNewIntent方法不會被調用
(3) singleTask
即棧內復用模式
這是一種單實例模式,在該模式下,只要Activity存在于棧中,不管是否位于棧頂,均不會重新創建Activity實例,且系統也會回調其onNewIntent方法
如果該Activity存在但不位于棧頂,則系統會彈出該Activity之上的所有Activity,將該Activity置于棧頂
(4)singleInstance
即單實例模式
該模式與singleTask模式類似,不過該模式下的Activity只能單獨地位于一個任務棧中
例如,如果Activity A是singleInstance模式,當A第一次啟動時,系統會為之創建一個任務棧,然后將A單獨置于該棧中,除非A被銷毀了,否則之后每次啟動A,都不會重新創建實例
指定啟動模式有兩種方法:
1.在AndroidManifest中直接為Activity指定啟動模式
<activity
android:name=".AnotherActivity"
android:launchMode="singleTask"
android:taskAffinity="com.custom.task" />
2.在Intent中設置標志位Flags來為Activity指定啟動模式
Intent intent = new Intent(this, AnotherActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
在優先級上,第二種方式的優先級要高于第一種,如果兩者同時存在,則以第二種為準
兩者在限定范圍上有所差別,第一種方式無法設定“Intent.FLAG_ACTIVITY_CLEAR_TOP”模式,第二種方式無法設定“singleInstance”模式
來自:http://www.jianshu.com/p/872a87fc5734