本文為Android的Activity相關(guān)知識整理,具體參考了
- 《Android開發(fā)藝術(shù)探索》第一章
- 《Android群英傳》第八章
- 《第一行代碼》第二章
Activity是與用戶交互的第一接口。
- Activity生命周期
Activity具有多種形態(tài)。其生命周期圖如下:
其中,有三個穩(wěn)定態(tài)予弧,其他都是過渡態(tài):
- Resumed 薛匪,此時打瘪,Activity處于棧頂历筝,處理用戶交互酗昼。
- Paused,當(dāng)Activity的一部分被擋住的時候進入這個狀態(tài)梳猪,不會與用戶交互麻削。
- Stopped,當(dāng)Activity完全被覆蓋時進入這個狀態(tài)春弥,此時Activity不可見呛哟,僅在后臺運行。
1.1 Activity經(jīng)典生命周期
(以下均指MainActivity的生命周期)
-
Activity啟動匿沛,點開一個應(yīng)用展示MainActivity
- onCreate()->onStart()->onResume() 到達(dá)Resumed狀態(tài)竖共,此時MainActivity被打開。
-
Activity暫停與恢復(fù)俺祠,1:打開了一個半屏幕的dialog公给,2:按返回鍵返回MainActivity
- onPause()到達(dá)Paused狀態(tài),此時dialog被打開蜘渣。
- onResume()到達(dá)Resumed狀態(tài)淌铐,此時dialog被關(guān)閉,顯示MainActivity
-
Activity停止與恢復(fù)蔫缸,1:打開另一個SecondActivity腿准,2:按返回鍵返回MainActivity
- onPause()->onStop()到Stopped狀態(tài),此時SecondActivity被打開拾碌。
- onRestart()->onStart()->onResume()到Resumed狀態(tài)吐葱,此時SecondActivity被關(guān)閉,重新回到MainActivity校翔。
- Activity銷毀弟跑,在MainActivity按返回鍵
- onPause()->onStop()->onDestory(),此時已經(jīng)銷毀 MainActivity防症。
注意:
- 在onPause()中一定要釋放使用的系統(tǒng)資源孟辑,比如Camera,sensor蔫敲,receiversK撬浴!
- 在onStop()中執(zhí)行更大奈嘿、更多CPU密集的關(guān)閉操作貌虾。比如寫入信息到數(shù)據(jù)庫。
- 當(dāng)系統(tǒng)長期處于onStopped狀態(tài)而且此時系統(tǒng)內(nèi)存緊張時裙犹,系統(tǒng)會回收此Activity尽狠,而此時衔憨,系統(tǒng)會通過onSaveInstanceState()方法將Activity狀態(tài)保存到Bundle對象中(finish()方法銷毀時不會保存)。當(dāng)重新創(chuàng)建此Activity時晚唇,保存的Bundle對象會傳遞到onRestoreInstanceState()與onCreate()中巫财。
- onCreate和onDestroy是配對的,分別標(biāo)識著Activity的創(chuàng)建和銷毀; onStart和onStop是配對的哩陕,標(biāo)識著Activity是否可見;onResume和onPause是配對的平项,標(biāo)識著Activity是否在前臺。
- 假設(shè)當(dāng)前Activity為MainAcitivty 悍及,這時用戶啟動SecondActivity 闽瓢,那么MainAcitivty 的onPause先執(zhí)行,SecondActivity 的onResume后執(zhí)行心赶。Android規(guī)定扣讼,不能在onPause中做重量級操作,就是基于這里缨叫。我們應(yīng)該盡量在onStop中做操作椭符,使得新Activity盡快顯示
- MainAcitivty->onPause
- SecondActivity->onCreate
- SecondActivity->onStart
- SecondActivity->onResume
- MainAcitivty->onStop
1.2 異常生命周期
以下幾種情況下,Activity的生命周期會發(fā)生異常
1耻姥,資源相關(guān)的系統(tǒng)配置發(fā)生改變
比如销钝,旋轉(zhuǎn)屏幕,在默認(rèn)狀態(tài)下琐簇,Activity就會被銷毀并且重新創(chuàng)建蒸健。
- 首先,Activity就會被銷毀婉商,onPause(), onStop(),onDestory()均會被調(diào)用似忧,同時由于Activity是在異常狀態(tài)下終止的,系統(tǒng)會在onStop()之前丈秩,調(diào)用onSaveInstanceState()來保存當(dāng)前Activity的狀態(tài)盯捌。
- 然后,Activity被重新創(chuàng)建癣籽,系統(tǒng)會在onStart()之后挽唉,調(diào)用onRestoreInstanceState(),并且把之前保存的Bundle對象傳遞給onRestoreInstanceState()和onCreate()方法筷狼。
- PS: 這兩個方法都能對Bundle數(shù)據(jù)進行處理,但是一般用 onRestoreInstanceState()匠童。因為onRestoreInstanceState()一旦被調(diào)用埂材,其參數(shù) Bundle savedInstanceState 一定是有值的,我們不必判斷其是否為空汤求。
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState != null){
String test = savedInstanceState.getString("extre_test");
Log.d(TAG, test);
}
}
@Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putString("extre_test", "test");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
super.onRestoreInstanceState(savedInstanceState);
String test = savedInstanceState.getString("extre_test");
Log.d(TAG, test);
}
執(zhí)行順序:
1 onPause()
2 onSaveInstanceState(Bundle outState)
3 onStop()
4 onDestory()
5 onCreate(Bundle savedInstanceState)
6 onStart()
7 onRestoreInstanceState(Bundle savedInstanceState)
注意:系統(tǒng)僅僅在Activity異常終止時才會調(diào)用onRestoreInstanceState()俏险。
2严拒,系統(tǒng)內(nèi)存不足
此時的系統(tǒng)中數(shù)據(jù)的存儲和恢復(fù)情況和上面第一種情況一致。Activity優(yōu)先級如下:
1 前臺Activity
2 可見但非前臺Activity
3 后臺Activity
當(dāng)系統(tǒng)資源不足的時候竖独,會按照這個優(yōu)先級使用onSaveInstanceState()和onRestoreInstanceState()來存儲和恢復(fù)數(shù)據(jù)裤唠。
還有一些后臺進程不是四大組件,這樣就會很容易被殺死~~ 一般是將這些后臺工作放入Service中莹痢,從而保證有一定的優(yōu)先級种蘸。
PS:阻止Activity被重新創(chuàng)建
使用android:configChanges="orientation|screenSize"屬性。
常用屬性:
- locale:設(shè)備的本地位置發(fā)生變化竞膳,一般指切換了系統(tǒng)語言航瞭。
- orientation:屏幕方向發(fā)生變化
- screenSize:屏幕大小發(fā)生變化,當(dāng)旋轉(zhuǎn)屏幕的時候坦辟,屏幕尺寸會變?睢!o弊摺滨彻!這個比較特殊,當(dāng)minSdkVersion和targetSdkVersion均低于13時挪蹭,此選項不會導(dǎo)致Activity重啟亭饵,否則會導(dǎo)致Activity重啟。
- keyboardHidden:鍵盤的可訪問性發(fā)生變化嚣潜,比如調(diào)出鍵盤冬骚。
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="orientation|screenSize|keyboardHidden" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
......//當(dāng)系統(tǒng)配置發(fā)上變換時,進行的工作
}
- Android任務(wù)棧
Android的APP通常會有多個Activity懂算,各個Activity之間通過Intent進行連接只冻,系統(tǒng)通過棧結(jié)構(gòu)來保存整個APP的Activity。系統(tǒng)有兩種方式控制Activity的啟動模式计技。
- AndroidMainifest的launchMode
- Intent Flag(優(yōu)先級更高)
2.1AndroidMainifest啟動模式
在AndroidMainifest.xml文件里面的activity標(biāo)簽設(shè)置啟動模式喜德。
<activity
android:name=".FirstActivity"
android:launchMode="singleTop"
android:label="This is FirstActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
-
standard,標(biāo)準(zhǔn)模式垮媒,每次都會創(chuàng)建新的Activity覆蓋在原Activity上
Paste_Image.png - singleTop舍悯,棧頂復(fù)用模式,首先判斷棧頂Activity是否是要啟動的Activity睡雇,如果是則不創(chuàng)建新的Activity而直接引用這個Activity萌衬;如果不是則創(chuàng)建新的Activity。
- singleTask它抱,棧內(nèi)復(fù)用模式秕豫,檢測整個Activity棧中是否存在當(dāng)前需要啟動的Activity,如果存在則將該Activity置于棧頂,并銷毀其上所有Activity混移。
- singleInstance祠墅,單實例模式,創(chuàng)建新的任務(wù)棧歌径,且該任務(wù)棧僅有一個Activity毁嗦。
TaskAffinity
taskAffinity速挑,任務(wù)相關(guān)性晨抡。xml中的一個屬性,標(biāo)識了一個Activity所需要的任務(wù)棧的名字失驶。默認(rèn)是包名勺届。如果設(shè)置了其他的名字如com.test.task1驶俊,那啟動它的時候就會新建一個名為com.test.task1的任務(wù)棧。
<activity
android:name="com.test.task0.MainActivity"
android:label="@string/app_name"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category andorid:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
andorid:name="com.test.SecondActivity"
android:taskAffinity="com.test.task1"
android:label="@string/app_name"
android:launchMode="singleTask"/>
<activity
andorid:name="com.test.ThirdActivity"
android:taskAffinity="com.test.task1"
android:label="@string/app_name"
android:launchMode="singleTask"/>
如果從MainActivity啟動SecondActivity免姿,然后再啟動ThirdActivity饼酿,那么任務(wù)棧如下:
com.test.task0 MainActivity
com.test.task1 SecondActivity ThirdActivity
若再從ThirdActivity啟動MainActivity,那么任務(wù)棧如下:
com.test.task0 MainActivity
com.test.task1 SecondActivity ThirdActivity MainActivity
2.2 Intent Flag啟動模式
對Intent進行設(shè)置
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
重要的Flag:
- FLAG_ACTIVITY_NEW_TASK胚膊,啟動的Activity在新的Task中故俐,相當(dāng)于android:launchMode="newTask"
- FLAG_ACTIVITY_SINGLE_TOP,相當(dāng)于android:launchMode="singleTop"
- FLAG_ACTIVITY_CLEAR_TOP紊婉,相當(dāng)于android:launchMode="singleTask"
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS药版,當(dāng)以此種模式啟動A,A再啟動B時喻犁,A會被銷毀槽片。等同于android:excludeFromeRecents="true"
3 IntentFilter的匹配規(guī)則
<activity android:name="SecondActivity">
<intent-filter>
<action android:name="android.intent.action.SEND">
<category android:name="android.intent.category.DEFAULT">
<data android:mimeType="text/plain">
</intent-filter>
</activity>
調(diào)用一個Activity主要包括兩種:
1,顯式調(diào)用肢础。
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
2, 隱式調(diào)用
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setDataAndType(Uri.parse("file://abc"), "text/plain");
startActivity(intent);
隱式調(diào)用需要Intent能夠匹配目標(biāo)組件的IntentFilter中的過濾信息还栓。
IntentFilter中的過濾信息有action, category, data。
只有一個Intent同時匹配這三個類別才能啟動目標(biāo)Activity传轰。
- action的匹配要求剩盒,Intent中的action存在且必須和過濾規(guī)則中的其中一個action相同。
- data的匹配要求慨蛙,Intent中的data存在且必須和過濾規(guī)則中的其中一個data相同辽聊。
- category匹配要求,如果含有category期贫,那么所有的category都必須和過濾規(guī)則中的其中一個category相同跟匆。也就是說它的category可以沒有!M场<致痢!2号痢(原因是默認(rèn)的category是android.intent.category.DEFAULT垢揩,不過得在activity標(biāo)簽的intent-filter中加入android.intent.category.DEFAULT這個category)
- 退出程序的兩種方法
4.1 利用Activity的singleTask模式退出
-
將主Activity設(shè)置為singleTask模式
<activity android:name=".MainActivity" android:launchMode="singleTask" ...
- 在要退出的Activity中轉(zhuǎn)到主Activity中,從而將主Activity之上的Activity都清除
Intent intent = new Intent(this, MainActivity.class); startActivity(intent);
- 然后重寫主Activity的onNewIntent()方法敛瓷,在方法中加上finish()叁巨,從而銷毀最后一個Activity。
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); finish(); }
4.2 利用專門的集合類對所有的活動進行管理
-
創(chuàng)建一個ActivityCollector 類作為活動管理器呐籽。List<Activity>作為存放活動的列表锋勺。
public class ActivityCollector { public static List<Activity> activities = new ArrayList<Activity>(); public static void addActivity(Activity activity) { activities.add(activity); } public static void removeActivity(Activity activity) { activities.remove(activity); } public static void finishAll() { for (Activity activity : activities) { if (!activity.isFinishing()) { activity.finish(); } } } }
-
創(chuàng)建一個繼承自Activity的BaseActivity 。ActivityCollector里存放的活動要隨著onCreate而添加狡蝶,隨著onDestory而銷毀庶橱。
public class BaseActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d("BaseActivity", getClass().getSimpleName()); ActivityCollector.addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); ActivityCollector.removeActivity(this); } }
全部的Activity都要繼承自BaseActivity,此時贪惹,只要調(diào)用
ActivityCollector.finishAll();
苏章,就可以銷毀所有Activity,關(guān)掉程序奏瞬。