異常情況下的生命周期
情況一:資源相關(guān)的系統(tǒng)配置發(fā)生改變導(dǎo)致Activity被銷毀并重建
比較典型的就是橫豎屏的切換伙判、字體大小的變化或者分辨率的變化荠藤,默認(rèn)情況下系統(tǒng)會(huì)將Activity銷毀并重建。此時(shí)系統(tǒng)會(huì)調(diào)用onSaveInstanceState方法保存Activity的狀態(tài)(判斷的標(biāo)準(zhǔn)是系統(tǒng)認(rèn)為Activity被銷毀后按灶,會(huì)立刻被重建),onSaveInstanceState的調(diào)用發(fā)生在onStop之前,與onPause的調(diào)用沒有固定的先后順序惹悄。Activity被重建時(shí),系統(tǒng)會(huì)調(diào)用onRestoreInstanceState方法肩钠,它的調(diào)用發(fā)生在onStart之后泣港,與onResume沒有固定的先后順序。在Activity被重建時(shí)价匠,可以選擇在onRestoreInstanceState或onCreate方法中讀取bundle數(shù)據(jù)恢復(fù)之前的Activity狀態(tài)当纱,唯一的區(qū)別是,如果onRestoreInstanceState被調(diào)用那參數(shù)中的bundle對(duì)象一定不會(huì)為null踩窖。
在異常情況下坡氯,系統(tǒng)銷毀Activity時(shí),會(huì)默認(rèn)保存一些視圖狀態(tài),比如ListView的滾動(dòng)位置箫柳,EditText輸入的內(nèi)容和聚焦?fàn)顟B(tài)手形。這是因?yàn)閂iew也有onSaveInstanceState和onRestoreInstanceState方法。當(dāng)Activity被銷毀時(shí)悯恍,首先觸發(fā)其onSaveInstanceState方法库糠,然后Activity會(huì)委托(或者叫分發(fā)任務(wù))它的window對(duì)象保存相關(guān)數(shù)據(jù),觸發(fā)其onSaveInstanceState方法涮毫,繼而window對(duì)象會(huì)委托它所包含的View層級(jí)曼玩,逐層觸發(fā)各個(gè)view的onSaveInstanceState方法,這樣就就保存狀態(tài)的任務(wù)窒百,分發(fā)到了Activity的視圖所包含的所有View對(duì)象黍判。
情況二 內(nèi)存不足導(dǎo)致系統(tǒng)需要銷毀低優(yōu)先級(jí)的Activity
Activity根據(jù)優(yōu)先級(jí)的高低,可以分為三種:
(1)前臺(tái)Activity——處于onResume狀態(tài)篙梢,正在與用戶交互
(2)可見Activity——處于onPause狀態(tài)顷帖,用戶可見,但是不能交互渤滞。比如從Activity觸發(fā)彈出對(duì)話框贬墩,此時(shí)Activity即為可見狀態(tài),但是不可交互妄呕。
(3)后臺(tái)Activity——處于onStop的狀態(tài)陶舞,Activity不可見,已經(jīng)暫停绪励。
系統(tǒng)內(nèi)存資源不足時(shí)肿孵,會(huì)按照優(yōu)先級(jí)去殺掉目標(biāo)Activity所在的進(jìn)程,并在后續(xù)通過onSaveInstanceState和onRestoreInstanceState方法來存儲(chǔ)和恢復(fù)數(shù)據(jù)疏魏。
系統(tǒng)配置項(xiàng)里有很多內(nèi)容停做,如果某個(gè)內(nèi)容項(xiàng)發(fā)生變化時(shí),我們不希望Activity被系統(tǒng)重建大莫,可以給Activity在manifest配置中指定configChanges屬性蛉腌。當(dāng)Activity指定了configChanges屬性后,相應(yīng)的系統(tǒng)配置項(xiàng)發(fā)生變化時(shí)只厘,Activity將不會(huì)被銷毀烙丛,onSaveInstanceState和onRestoreInstanceState方法也不會(huì)被調(diào)用,取而代之的是羔味,系統(tǒng)會(huì)調(diào)用Activity的onConfigurationChanged方法河咽。
Activity啟動(dòng)模式
standard——默認(rèn)的標(biāo)準(zhǔn)模式。關(guān)于standard介评,值得注意的是库北,如果用ApplicationContext.startActivity啟動(dòng)standard模式的Activity爬舰,就會(huì)報(bào)錯(cuò)。原因是非Acitivity類型的context對(duì)象沒有任務(wù)棧(task)寒瓦,而standard模式的Acitivity默認(rèn)是要進(jìn)入啟動(dòng)它的Activity所在的任務(wù)棧情屹。解決的方法就是,啟動(dòng)Activity時(shí)杂腰,給Intent設(shè)置FLAG_ACTIVITY_NEW_TASK標(biāo)識(shí)垃你,這樣實(shí)際上相當(dāng)于將Activity的啟動(dòng)模式變?yōu)閟ingleTask。
singleTop——值得注意的是onNewIntent觸發(fā)時(shí)機(jī)喂很。比如重復(fù)調(diào)用在棧頂?shù)膕ingleTop模式的Activity惜颇,只有三個(gè)生命周期方法會(huì)被觸發(fā),onPause->onNewIntent->onResume少辣。
應(yīng)用場(chǎng)景:網(wǎng)易新聞凌摄。
假設(shè)主界面為 MainActivity,顯示新聞的界面是 DetailActivity漓帅,顯然顯示任何一條新聞都會(huì)使用 DetailActivity锨亏,即把新聞內(nèi)容通過 Intent 傳給 DetailActivity 就可以了。
假設(shè)你正在看新聞1(即在 DetailActivity)忙干,此時(shí)手機(jī)收到服務(wù)器的推送:收到一條通知(新聞2)器予,點(diǎn)擊通知就會(huì)跳轉(zhuǎn)到 DetailActivity 并顯示新聞2,當(dāng)你點(diǎn)擊通知時(shí)捐迫,因?yàn)槟壳皸m數(shù)?Activity 就是 DetailActivity乾翔,因此這里就是使用 SingleTop 的地方,即點(diǎn)擊通知后以 SingleTop 加載模式打開 DetailActivity 并顯示新聞2施戴,因此新聞1的 DetailActivity 就被覆蓋掉了反浓。
此后你點(diǎn)擊返回鍵會(huì)回到主界面。
singleTask——模式為singleTask的Activity啟動(dòng)時(shí)暇韧,并不一定會(huì)新建一個(gè)Task勾习。具體取決于taskAffinity的參數(shù)浓瞪。比如懈玻,只是給Activity設(shè)置了singleTask。而沒有指定taskAffinity乾颁,則該Activity會(huì)歸入到默認(rèn)的Task(名稱為包名)涂乌。當(dāng)singleTask模式的Activity啟動(dòng)時(shí),如果taskAffinity指定的Task已經(jīng)存在英岭,就不會(huì)新建Task湾盒。
應(yīng)用場(chǎng)景:微信的主界面(一般應(yīng)用主界面都會(huì)以 SingleTask 啟動(dòng))。
你打開微信主界面(在棧的最底部)后诅妹,進(jìn)入朋友圈(在棧的頂部)罚勾,此時(shí)你點(diǎn)擊 Home 鍵回桌面毅人,并打開網(wǎng)易新聞。
假設(shè)你想將網(wǎng)易新聞的一條新聞分享給微信好友尖殃,那么就按照 分享->微信->好友A->分享給他->留在微信丈莺。接著會(huì)跳轉(zhuǎn)微信的主界面,即不是你原本所在的朋友圈送丰,并且微信的棧只剩下一個(gè)元素:主界面的 Activity缔俄。這里就使用了 SingleTask(即以 SingleTask 加載模式打開微信主界面)。
singleInstance——Activity和所在的Task只會(huì)被創(chuàng)建一次器躏,具有全局唯一性俐载,之后每次啟動(dòng)都會(huì)復(fù)用這個(gè)Activity實(shí)例。而singleInstance模式的Activity啟動(dòng)其他Activity登失,等同于自動(dòng)標(biāo)記為FLAG_ACTIVITY_NEW_TASK遏佣。
應(yīng)用場(chǎng)景:鬧鈴的響鈴界面。
系統(tǒng)電話撥號(hào)界面揽浙,無論從任何其他應(yīng)用跳轉(zhuǎn)到撥號(hào)界面贼急,你會(huì)發(fā)現(xiàn),之前輸入的電話號(hào)碼一直都在捏萍,即表示此Acitivity始終是同一實(shí)例被復(fù)用太抓。
FLAGS
FLAG_ACTIVITY_NEW_TASK——等價(jià)于將Activity的啟動(dòng)模式設(shè)置為singleTask。
FLAG_ACTIVITY_SINGLE_TOP——等價(jià)于將Activity的啟動(dòng)模式設(shè)置為singleTop令杈。
FLAG_ACTIVITY_CLEAR_TOP——值得注意的是走敌,如果被啟動(dòng)的Activity是standard模式,那么它本來的實(shí)例和在它之上的Activity都會(huì)被出棧逗噩,然后系統(tǒng)會(huì)重新創(chuàng)建該Activity的實(shí)例
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS——具備這個(gè)標(biāo)識(shí)的Activity不會(huì)出現(xiàn)在歷史Activity的列表中掉丽,等同于在manifest中為Activity設(shè)置android:excludeFromRecents="true"。(但是這個(gè)標(biāo)識(shí)在不同版本的Android系統(tǒng)中异雁,不一定起作用捶障,已經(jīng)有人在stackoverflow發(fā)問過,google的開發(fā)人員有過回應(yīng)纲刀,需要修復(fù)這個(gè)問題项炼。)
task和back stack
task是一組Activity的集合,back stack是task的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)示绊。task是可以跨應(yīng)用锭部,跨進(jìn)程的。
taskAffinity和allowTaskReparenting
taskAffinity可以理解為一個(gè)task的名稱面褐。主要和singleTask啟動(dòng)模式或者allowTaskReparenting配對(duì)使用拌禾。
一個(gè)task的affinity取決于棧中最底部的Activity(root Activity)的taskAffinity屬性。如果沒有指定展哭,默認(rèn)是和包名相同湃窍。
taskAffinity屬性相同的Activity在同一個(gè)task闻蛀。同時(shí)也意味著,我們可以根據(jù)taskAffinity屬性將同一個(gè)應(yīng)用Activity分組到不同的task您市,更重要的是循榆,也可以將不同應(yīng)用的Activity分組到同一個(gè)task,只要taskAffinity屬性值相同墨坚。如果將Activity的taskAffinity屬性設(shè)置為空字符串秧饮,表示這個(gè)Activity不屬于任何task。
taskAffinity屬性和allowTaskReparenting屬性配對(duì)使用泽篮,會(huì)產(chǎn)生特殊的效果盗尸。當(dāng)應(yīng)用A啟動(dòng)了應(yīng)用B的Activity,此時(shí)如果taskAffinity沒有特指帽撑,那么Activity會(huì)在應(yīng)用A的任務(wù)棧中泼各。若allowTaskReparenting屬性設(shè)置為true,則當(dāng)應(yīng)用B啟動(dòng)時(shí)亏拉,Activity將從應(yīng)用A的任務(wù)棧轉(zhuǎn)移到應(yīng)用B的任務(wù)棧扣蜻。
intentFilter的匹配規(guī)則
action的匹配規(guī)則要求Intent中action不能為空,且必須與過濾規(guī)則的其中一個(gè)action匹配及塘,且區(qū)分大小寫莽使。
category,intent中可以設(shè)置多個(gè)category笙僚,對(duì)于每一個(gè)category都必須與過濾規(guī)則的其中一個(gè)category匹配芳肌。intent也可以沒有category,因?yàn)橄到y(tǒng)在隱式啟動(dòng)Activity時(shí)肋层,會(huì)默認(rèn)加上“android.intent.category.DEFAULT”這個(gè)category亿笤。所以我們要在接受隱式調(diào)用的intentFilter中加上“android.intent.category.DEFAULT”這個(gè)category。
data栋猖,和action的匹配規(guī)則相同净薛。data有mimeType和URI兩部分組成。URI由包括scheme蒲拉、host肃拜、port、path等全陨。如果data中沒有指定URI爆班,系統(tǒng)會(huì)默認(rèn)URI為content和file。也就是說辱姨,雖然不打算指定URI,intent中data的URI部分的scheme也必須是content或者file才能匹配戚嗅,這點(diǎn)需要注意雨涛。另外枢舶,要為intent指定完整的data,應(yīng)該調(diào)用setDataAndType替久,不能先調(diào)用setData凉泄,再調(diào)用setType。因?yàn)檫@兩個(gè)方法彼此會(huì)清除對(duì)方的值蚯根。
intentFilter對(duì)Service和boardcastReceiver的匹配規(guī)則也是同樣的道理后众,只是系統(tǒng)建議對(duì)Service的啟動(dòng)盡量用顯示調(diào)用。
判斷一個(gè)隱式intent是否有相匹配的Activity可以啟動(dòng)颅拦,有兩個(gè)方法:
1 PachageManager的resolveActivity或者intent的resolveActivity方法蒂誉,返回的是最佳匹配的Activity,如果找不到匹配的Activity會(huì)返回null距帅。
2 PackageManager的queryIntentActivities方法右锨,返回的是所有匹配的Activity信息。
resolveActivity和queryIntentActivities方法都要傳兩個(gè)參數(shù)碌秸。其中第二個(gè)參數(shù)flags需要注意的是绍移,MATCH_DEFAULT_ONLY,這個(gè)標(biāo)識(shí)的意思是僅僅匹配那些在intent-filter中聲明了“android.intent.category.DEFAULT”這個(gè)category的的Activty讥电。使用這個(gè)標(biāo)識(shí)位的意義是蹂窖,只要resolveActivity方法不返回null,startActivity就一定可以成功恩敌。