在這篇文章中扫茅,我會繼續(xù)跟大家分享有關(guān)于Android中啟動模式的相關(guān)知識。當然非迹,如果對這個啟動模式還不完全了解或者沒有聽過的話抡谐,可以先看看我之前寫的有關(guān)于這個知識點的入門篇Android的啟動模式(上)。好了俏蛮,言歸正傳撑蚌,在上一篇已經(jīng)介紹過,activity在棧中默認不能重排搏屑,因此争涌,應(yīng)用中的一個activity可能被多次實例化并且壓入同一個棧中,如圖所示:
如果此時使用back鍵返回辣恋,activity的每個實例都將會按照打開的順序重新出現(xiàn)亮垫。這勢必會導致用戶生體驗效果,因此要改變這種現(xiàn)象或者解決上篇末尾提到的問題伟骨,對啟動模式的了解必不可少饮潦,當然,若想了解得更加透徹的話底靠,歡迎訪問官方文檔:Tasks and Back Stack害晦。
定義啟動模式
總的來說特铝,啟動模式?jīng)Q定了你的activity和task的關(guān)聯(lián)性暑中。當一個activity啟動的時候壹瘟,有兩種方式指定它與task的關(guān)聯(lián):使用manifest文件和使用intent flag。當intent的flag和manifest所指定的啟動模式發(fā)生沖突的時候鳄逾,此時就以intent flag指定的模式為準稻轨。
-
使用manifest清單文件設(shè)置啟動模式
通過在manifest文件<activity>中添加launchMode屬性來指定啟動模式:
standard,默認的LaunchMode雕凹,也是最容易理解的殴俱。如果某個Activity使用該 LaunchMode, 當這個Activity啟動時枚抵,系統(tǒng)會創(chuàng)建一個該Activity的新的實例线欲,并且傳遞 一個intent給它。該 Activity可以被實例化多次汽摹,各個實例可以屬于不同的Task李丰,一個Task 中也可以存在多個實例。
singleTop逼泣,如果這個Activity有一個實例已經(jīng)存在于當前Task的頂部趴泌,那么系統(tǒng)就會傳 遞一 個intent給這個實例的onNewIntent()方法,而不會去重新創(chuàng)建一個新的Activity實例拉庶。 這個 Activity也可以被實例化多次嗜憔,每個實例可以屬于不同的task,但只有當Back Stack棧頂 的Activity實例不是該Activity的實例時氏仗,一個task中也可以存在多個實例吉捶。應(yīng)該注意的是,
當一個Activity的新實例創(chuàng)建完畢后皆尔,用戶可以按返回鍵返回前一個activity帚稠。但是當 Activity已有實例正在處理剛到達的intent時,用戶無法用返回鍵回到onNewIntent()中 intent到來之前的Activity 狀態(tài)床佳。singleTask滋早,系統(tǒng)創(chuàng)建一個新的Task,并且實例化這個Activity作為這個Task的根 Activity砌们。然而杆麸,若這個Activity已經(jīng)存在了一個實例在一個Task中,那么系統(tǒng)就會將這個 Intent傳遞到這個Activity的onNewIntent()方法浪感,而不是去重新創(chuàng)建一個實例昔头。同一個時 間,只允許存在一個這樣的Activity影兽。注意的是揭斧,雖然系統(tǒng)創(chuàng)建了一個新的Task,但是只要按 下返回鍵還是會回到原來的Activity
singleInstance,和"singleTask"類似讹开,不同的是盅视,系統(tǒng)不會再該activity實例的task中,啟動任何其他Activity到這個task中旦万。這個Activity是它所在的task中唯一的成員闹击。任何有這個activity啟動的Activity都會放入到另外一個task中。
對于返回處理成艘,不管activity是在一個新的task啟動赏半,還是在當前task中啟動,只要一按下返回鍵淆两,就會返回到之前的那個activity中断箫。但是也是存在一種情況例外,就是當你啟動一個啟動模式設(shè)為singleTask的Activity時秋冰,如果這個activity在一個后臺task中存在實例瑰枫,那個這整個task將會被放置到前臺,這時候丹莲,back stack就會包含這個task中所有的activities光坝,并且它們都是放在棧頂。如下圖:
-
使用Intent的flag設(shè)置啟動模式
當你啟動一個Activity時甥材,你也可以動態(tài)的設(shè)置intent的flag盯另,然后通過startActivity()方法啟動activity,從而修改其啟動的activity與它的task的關(guān)聯(lián)模式洲赵。具體可以使用的flag有:
FLAG_ACTIVITY_NEW_TASK:對應(yīng)之前的“singleTask”鸳惯,在新的task中啟動activity,如果一個你需要的activity的task已經(jīng)存在叠萍,則將它推向前臺芝发,恢復其上一個狀態(tài),它通過onNewIntent()收到這個新的intent苛谷。
FLAG_ACTIVITY_SINGLE_TOP:對應(yīng)之前的“singleTop”,如果被啟動的activity是當前頂部的activity辅鲸,則已經(jīng)存在的實例會收到onIntent(),而不會重新去創(chuàng)建這個實例。
FLAG_ACTIVITY_CLEAR_TOP:這個行為在launchMode屬性中沒對應(yīng)的屬性值腹殿,若被啟動的activity已經(jīng)在當前task中運行独悴,則不會創(chuàng)建它的新實例,而是的銷毀在它之上的其他所有的activities锣尉,然后通過 onNewIntent()傳遞一個新的intent給這個恢復了的activity刻炒,它一般會與FLAG_ACTIVITY_NEW_TASK一起使用。值得注意的是自沧,如果activity的啟動模式是"standard"坟奥,它自己也將被移除,然后一個新的實例將被啟動。這是因為當啟動模式是"standard"時爱谁,為了接收新的intent必須創(chuàng)建新的實例晒喷。
任務(wù)共用性的處理(Handling affinities)
一般來說,singleTask就是開啟一個新的Task管行,但是在實際使用過程中厨埋,我們有時會發(fā)現(xiàn)邪媳,有時候并不是這樣的捐顷,這是因為我們定義了affinities,也就是任務(wù)公用性。
affinity定義了一個Activity將被分配到哪一個Task中雨效。默認情況下迅涮,同一個app中得所有activity有一個同樣的affinity,因此徽龟,默認情況下同一個應(yīng)用程序中得所有activity都在同一個task中叮姑。一個Task的affinity由這個Task的根Actiivty決定。然而据悔,我們可以修改Activity默認的affinity传透,這樣,不同應(yīng)用程序的Activity可以共用同樣的affinity极颓,或者同一個程序的不同Activity分配不同的affinity朱盐。一個應(yīng)用程序默認的affinity就是應(yīng)用程序的包名,所以菠隆,如果我們想定義一個不同的affinity兵琳,必須和默認的affinity不同。我們可以通過<application>中得android:taskAffinity修改整個程序的affinity骇径,也可以通過<activity>的android:taskAffinity對單個Activity的affinity修改躯肌。
而affinity的使用通常有以下兩種情況,
-
當android:launchMode是singleTask或者Intent中包含F(xiàn)LAG_ACTIVITY_NEW_TASK:
默認情況下破衔,我們調(diào)用startActivity()清女,會實例化一個Activity,放入到與調(diào)用者相同的task晰筛。但是如果這個Activity的的啟動模式是singleTask校仑,或者啟動它的Intent包含了
FLAG_ACTIVITY_NEW_TASK時,系統(tǒng)會進行如下的步驟:- 判斷這個Activity有沒有實例已經(jīng)存在了传惠,有的話迄沫,直接傳遞Intent到它的onNewIntent()方法中。
- 如果不存在卦方,系統(tǒng)查找是否有與這個Activity相同affinity的Task已經(jīng)存在羊瘩,如果存在,那么就將這個Activity啟動到這個Task中。
- 如果不存在這樣的Task尘吗,那么系統(tǒng)就會創(chuàng)建一個新的Task逝她,并且將這個Activity啟動這個Task中,作為根Activity睬捶。
因此黔宛,從現(xiàn)在看來,只是單純的用singleTask指定Activity擒贸,是不能開辟一個新的Task的臀晃,因為我們并沒有給他指定affinity。而官方文檔對于singleTask的描述介劫,都是基于我們使用了不同的affinity的前提下徽惋,只不過是省略了這個描述。所以座韵,我們要明白险绘,singleTask的正確用法,應(yīng)該是結(jié)合affinity使用的誉碴。
-
當一個Activity設(shè)置allowTaskReparenting屬性為true:
這個屬性定義了一個Activity宦棺,表示是否可以從一個啟動它的task,切換到與它相同affinity的task中里去(當這個task切換到前臺的時候)黔帕。true表示可以移動代咸,false表示它必須呆在啟動他得task里。
通常情況下蹬屹,當一個Activity啟動了侣背,那么它就會存在于啟動它的task中,并且在整個生命周期中都留在這個task中慨默。但是贩耐,我們可以通過這個屬性,做出如下改變厦取,當這個Activity當前的Task處于后臺潮太,這個時候如果有一個該Activity具有相同affinity的Task被啟動到前臺,那么這個Activity就可以從它之前的Task虾攻,移動到這個新的Task顯示铡买。
通常,它的作用是將app中得Activity與app的main task結(jié)合起來霎箍,舉個例子奇钞,如下:
有一個e-mail程序,他需要調(diào)用瀏覽器程序的某個Activity(假設(shè)為Activity A)來顯示一些數(shù)據(jù)漂坏,這個Activity A的該屬性設(shè)置為true【鞍#現(xiàn)在媒至,e-mail程序調(diào)用了這個Activity A,在用戶看來谷徙,好像這個Activity A就是e-mial程序的一部分拒啰,因為這個Activity A和這個e-mail程序在同一個task中。現(xiàn)在將e-mail退出到后臺完慧,啟動瀏覽器程序谋旦,因為Activity A和瀏覽器程序有相同的affinity,所以Activity A從e-mail程序的Task移動到瀏覽器程序的Task屈尼,并顯示在前臺册着。當我們下次再啟動e-mail程序時,Activity A就不會存在鸿染,因為他已經(jīng)移動到瀏覽器程序的Task里去了指蚜。
清理Back Stack
如果用戶離開一個task很久乞巧,系統(tǒng)就會清理這個task中除了根activity之外的所有activities涨椒。當用戶返回到這個task,只有根activity會被恢復绽媒。但是我們可以設(shè)置一些activity的屬性蚕冬,用來改變這一行為:
-
alwaysRetainTaskState
如果這個屬性在task的根activity中被設(shè)置為true,那么上面描述的默認行為不會發(fā)生是辕,即便過了很長時間囤热,task仍將會保持所有的activities。
-
clearTaskOnLaunch
如果這個屬性在task的根activity中被設(shè)置為true获三,每次用戶離開這個task旁蔼,整個task都會被清到只剩根activity,這樣用戶只會永遠返回到它最初的狀態(tài)疙教,即便離開的時間很短棺聊。
-
finishOnTaskLaunch
這個屬性和上一個很像,不同的是贞谓,它只作用于單個activity限佩,而不是整個task。它可以引起任何activity離開裸弦,包括根activity祟同。當它被設(shè)置為true時,這個activity只在當前會話中屬于這個task理疙,如果用戶離開后再返回晕城,它也不會再出現(xiàn)。
開啟一個Task
我們可以通過一個Activity指定一個intent過濾器窖贤,如下面:
<activity ...>
<intent-filter ... >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.inp tent.category.LAUNCHER" />
</intent-filter>
...
</activity>
這時這個activity就作為根activity存在于一個task中砖顷,這個activity也是進入這個task的入口點暇矫,同時,它的圖標和標簽也會被顯示在應(yīng)用啟動界面上择吊,這時用戶就可以啟動這個activity并且再次回到這個任務(wù)李根。因此,從這里我們可以看到几睛,要使用“singleTask”與“singleInstance”房轿,就必須這個activity應(yīng)當也有ACTION_MAIN與CATEGORY_LAUNCHER過濾器。因為假如沒有設(shè)置這兩個過濾器的話所森,當一個intent啟動一個"singleTask"的activity,在新的Task中進行初使化囱持,運行一段時間后,用戶突然按上了home鍵回到桌面焕济,此時這個Task就被移到后臺并且不可見纷妆,因為這個activity沒有設(shè)置過濾器,所以不是應(yīng)用啟動的activity晴弃,那么用戶也就無法返回到這個Task中了掩幢。
總結(jié)
今天的分享也差不多接近尾聲了,有關(guān)Android的啟動模式的主要部分的分析也大多涉及到了上鞠。我想际邻,如果大家想要全面地了解Android的啟動模式的話,我希望可以堅持看完這兩篇文章芍阎,我相信世曾,看完后你對Android的啟動模式還有工作棧的理解應(yīng)該有了很大的提高,對activity與Task的操作也會更加得心應(yīng)手谴咸。當然轮听,如果你覺得文章中有什么寫得錯誤或者不了解的地方,歡迎留言交流岭佳,謝謝!