注:此篇筆記只記錄重難點周霉,對于基礎(chǔ)和詳細(xì)內(nèi)容請自行學(xué)習(xí)《Android開發(fā)藝術(shù)探索》
1.1 Activity的生命周期
1.1.1 典型情況下的生命周期
onStart和onResume的區(qū)別是onStart可見忽洛,還沒有出現(xiàn)在前臺嚼鹉,無法和用戶進(jìn)行交互。onResume獲取到焦點可以和用戶交互诱篷。
當(dāng)啟動新Activity是透明主題時甚纲,舊Activity不會走onStop绞吁。
Activity切換時,舊Activity的onPause會先執(zhí)行裂七,然后才會啟動新的Activity皆看。
1.1.2 異常情況下的生命周期
Activity在異常情況下被回收時,onSaveInstanceState方法會被回調(diào)背零,回調(diào)時機(jī)是在onStop之前腰吟,當(dāng)Activity被重新創(chuàng)建的時候,onRestoreInstanceState方法會被回調(diào)徙瓶,時序在onStart之后毛雇;
當(dāng)Activity異常情況下的情況下需要重新創(chuàng)建時嫉称,系統(tǒng)會默認(rèn)我們保存當(dāng)前Activity的視圖結(jié)果,并在Activity重啟后為我們恢復(fù)這些數(shù)據(jù)灵疮,比如文本框用戶輸入的數(shù)據(jù)织阅,ListView的滾動位置等,具體可以查看相對應(yīng)View的源碼震捣,查看onSaveInstanceState和onRestoreInstanceState方法蒲稳。
關(guān)于保存和恢復(fù)View的層次結(jié)構(gòu),系統(tǒng)的工作流程:首先Activity會調(diào)用onSaveInstanceState去保存數(shù)據(jù)伍派,然后Activity會委托Window去保存數(shù)據(jù)江耀,接著Window會委托它上面的頂級容器去保存數(shù)據(jù),頂層容器一般是DecorView(ViewGroup)诉植;最后頂層容器再去一一通知它的子元素保存數(shù)據(jù)祥国,這樣整個數(shù)據(jù)的保存過程就完成了。
如果資源內(nèi)存不足優(yōu)先級低的Activity會被殺死晾腔,優(yōu)先級從高到低:
前臺Activity-正在和用戶交互優(yōu)先級最高舌稀;
可見但非前臺的Activity-比如Activity中彈了一個對話框,導(dǎo)致Activty可見但位于后臺灼擂;
后臺Activity-已經(jīng)被暫停的Activity壁查,已經(jīng)執(zhí)行了onStop,優(yōu)先級最低剔应。
如果我們不想配置發(fā)生改變就Activity重新創(chuàng)建睡腿,可以使用Activity的configChanges屬性;常用的有l(wèi)ocale峻贮、orientation和keyboardHidden這三個選項席怪;指定了configChanges后,Activity發(fā)生對應(yīng)改變后纤控,不會重啟Activity挂捻,只會調(diào)用onConfigurationChanged方法。
1.2 Activity的啟動模式
1.2.1 Activity的LaunchMode
任務(wù)棧是一個“后進(jìn)先出”的棧結(jié)構(gòu)船万,每次finish()處于前臺的Activity就會出棧刻撒,直到棧為空為止,當(dāng)棧中無任何Activity的時候耿导,系統(tǒng)就會回收這個任務(wù)棧声怔。
standard:標(biāo)準(zhǔn)模式,系統(tǒng)默認(rèn)模式碎节,每次啟動都會創(chuàng)建一個新的實例捧搞;在這種模式下,誰啟動了這個Activity,這個Activity就在啟動它的那個Activity所在的棧中胎撇。當(dāng)我們使用ApplicationContext去啟動standard模式的Activity就會報錯介粘,因為standard模式的Activity會默認(rèn)進(jìn)入啟動它的Activity所屬的任務(wù)棧中,而非Activity類型的Context并沒有任務(wù)棧晚树。解決的辦法是為這個待啟動的Activity指定FLAG_ACTIVITY_NEW_TASK標(biāo)記位姻采,這樣啟動的時候就會為它創(chuàng)建一個新的任務(wù)棧铆帽。
singleTop: 棧頂復(fù)用模式委刘。這種模式下,如果新的Activity已經(jīng)位于棧頂狰贯,那么此Activity不會創(chuàng)建宝鼓,同時他的onNewIntent方法會被回調(diào)刑棵,onCreate、onStart不會被調(diào)用愚铡。如果新的Activity的實例存在但不是位于棧頂蛉签,那么新的Activty依然會重新創(chuàng)建。
singleTask: 棧內(nèi)復(fù)用模式沥寥。這是一種單實例模式碍舍,這種模式下,Activity在一個棧中存在邑雅,那么多次啟動該Activity都不會重新創(chuàng)建實例片橡,和sinleTop一樣,系統(tǒng)會回調(diào)其onNewIntent淮野。如果啟動的Activity沒有所需要的任務(wù)棧捧书,就會先創(chuàng)建任務(wù)棧再創(chuàng)建Activity。singleTask默認(rèn)具有clearTop的效果录煤,具有該模式的Activity會讓其之上的Activity全部出棧鳄厌。
singleInstance: 單實例模式。這是一種加強(qiáng)的singleTask模式妈踊,除了具備singleTask的特性之外,具有該模式的Activity只能單獨位于一個任務(wù)棧中泪漂;比如Activity A是singleInstance模式的廊营,當(dāng)A啟動后,系統(tǒng)會為它創(chuàng)建一個新的任務(wù)棧萝勤,后續(xù)的啟動均不會創(chuàng)建新的Activity露筒,除非這個任務(wù)棧被系統(tǒng)銷毀了。
參數(shù)TaskAffinity用于指定Activity棧敌卓,TaskAffinity屬性經(jīng)常和singleTask啟動模式或allowTaskReparenting屬性配對使用慎式,其他情況沒有意義。當(dāng)應(yīng)用A啟動了應(yīng)用B的某個Activity后,如果這個Activity的allowTaskReparenting屬性為true的話瘪吏,那么當(dāng)應(yīng)用B被啟動后癣防,此Activity會直接從應(yīng)用A的任務(wù)棧轉(zhuǎn)移到應(yīng)用B的任務(wù)棧中。
兩種方法都可以為Activity指定啟動模式掌眠,但還是有區(qū)別蕾盯。
Activity的啟動模式可以通過AndroidMenifest為其指定啟動模式
<activity android:name=".MainActivity" android:launchMode="singleTask" />
- 還可以通過在Intent中設(shè)置標(biāo)記位來為Activity指定啟動模式
Intent intent = new Intent(this,MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
第二種方式的優(yōu)先級高于第一種,如果都設(shè)置了只有第二種會生效蓝丙。第一種方式無法直接為Activity設(shè)置FLAG_ACTIVITY_CLEAR_TOP標(biāo)記级遭,而第二種方式無法為Activity指定singleInstance模式。
通過adb shell dumpsys activity命令可以詳細(xì)的了解當(dāng)前任務(wù)棧情況渺尘。
1.2.2 Activity的Flags
FLAG_ACTIVITY_NEW_TASK為Activity指定“singleTask”啟動模式挫鸽,其效果和xml中指定該啟動模式相同。
FLAG_ACTIVITY_SINGLE_TOP為Activity指定“singleTop”啟動模式鸥跟,其效果和xml中指定該啟動模式相同掠兄。
FLAG_ACTIVITY_CLEAR_TOP具有此標(biāo)記位的Activity,當(dāng)他啟動時锌雀,在同一個任務(wù)棧中所有位于它上面的Activity都要出棧蚂夕。這個模式一般與FLAG_ACTIVITY_NEW_TASK配合使用,singleTask啟動模式默認(rèn)具有此標(biāo)記為的效果腋逆。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS具有這個標(biāo)記為的Activity不會出現(xiàn)在歷史Activity的列表中婿牍,當(dāng)某些情況不希望用戶通過歷史列表回到我們Activity的時候這邊標(biāo)記比較有用。等同于在XML中指定Activity的屬性android:excludeFromRecents="true"惩歉。
1.3 IntentFilter的匹配規(guī)則
啟動Activity分為兩種等脂,顯式調(diào)用(明確地指定被啟動對象的組件信息,包括包名和類名)和隱式調(diào)用(不需要明確指定組件信息撑蚌,需要Intent能匹配上目標(biāo)組件的IntentFilter中所設(shè)置的過濾信息)上遥。IntentFilter的過濾信息有action、category争涌、data粉楚。為了匹配過濾列表,需同時匹配過濾列表中的action亮垫、category模软、data信息,否則匹配失斠省燃异;一個Activity中可以有多個intent-filter,一個Intent只要能匹配任何一組intent-filter即可成功啟動對應(yīng)的Activity继蜡。
action匹配規(guī)則:要求intent中的action存在且必須和過濾規(guī)則中的其中一個相同回俐,區(qū)分大小寫逛腿。
category匹配規(guī)則:系統(tǒng)會默認(rèn)加上一個android.intent.category.DEAFAULT,所以intent中可以不存在category仅颇,但如果存在就必須匹配其中一個单默。同時為了我們的Activity能夠支持隱式調(diào)用,就必須要在intent-filter中指定“android.intent.category.DEFAULT”這個category灵莲。
data匹配規(guī)則:data由兩部分組成雕凹,mimeType和URI,要求和action相似政冻。如果沒有指定URI枚抵,URI但默認(rèn)值為content和file(schema)
如果要為Intent指定完整的data,必須調(diào)用setDataAndType方法明场,不能先調(diào)用setData再調(diào)用setType汽摹,因為這兩個方法都會清除對方的值。
采用PackageManager的resolveActivity方法或Intent的resolveActivity方法苦锨,如果找不到匹配的Activity就會返回null逼泣,我們通過判斷返回值就可以規(guī)避拋出android.content.ActivityNotFoundException異常。
在intent-filter中聲明了<category android:name="android.intent.category.DEFAULT"/>這個category的Activity舟舒,才可以接收隱式意圖拉庶。
有一類action和category的共同作用是標(biāo)明這是一個入口Activity。
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />