文章轉(zhuǎn)自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0520/2897.html
編輯推薦:稀土掘金愤估,這是一個針對技術(shù)開發(fā)者的一個應(yīng)用,你可以在掘金上獲取最新最優(yōu)質(zhì)的技術(shù)干貨磷仰,不僅僅是Android知識嚣崭、前端、后端以至于產(chǎn)品和設(shè)計都有涉獵删顶,想成為全棧工程師的朋友不要錯過!
英文原文:Understand Android Activity's launchMode: standard, singleTop, singleTask and singleInstance另外關(guān)于啟動模式還有篇很好的文章:Android中Activity四種啟動模式和taskAffinity屬性詳解
Activity是安卓上最聰明的設(shè)計之一闭专,優(yōu)秀的內(nèi)存管理讓多任務(wù)完美運(yùn)行在最流行的操作系統(tǒng)之上奴潘。并不是讓Activity在屏幕上啟動就完事了旧烧,其啟動方式也是需要關(guān)注的。這個話題的內(nèi)容很多画髓,其中很重要的就是啟動模式(launchMode)掘剪。這也是我們這篇博客要討論的內(nèi)容。
因?yàn)椴煌腁ctivity有不同的目的奈虾。有些被設(shè)計成每發(fā)送一個intent都單獨(dú)一個Activity工作夺谁,比如郵件客戶端中撰寫郵件的Activity,而有些則被設(shè)計成單例的肉微,比如郵件收件箱的Activity匾鸥。
這就是為什么指明一個Activity是否需要新建還是使用現(xiàn)有Activity是很有必要的,否則可能導(dǎo)致糟糕的用戶體驗(yàn)碉纳。多虧了安卓的核心工程師勿负,讓launchMode可以幫助你專門應(yīng)對這種情況。
設(shè)置一個launchMode
一般地劳曹,我們可以直接在AndroidManifest.xml 標(biāo)簽的一個屬性中設(shè)置launchMode奴愉,如下:
android:name=".SingleTaskActivity"
android:label="singleTask?launchMode"
android:launchMode="singleTask">
有4種類型的launchMode,我們一個一個的看铁孵。
standard
這是默認(rèn)的模式锭硼。
這種模式下,當(dāng)Intent發(fā)送的時候蜕劝,Activity總是被創(chuàng)建一個新的出來單獨(dú)工作檀头。想象一下,如果有發(fā)送10個撰寫郵件的Intent熙宇,那么將有10個不同的Activity啟動鳖擒。
在Lollipop之前設(shè)備上的表現(xiàn)
這種Activity將被創(chuàng)建并置于棧頂,和發(fā)送intent的Activity處于同一個任務(wù)中烫止。注:一般來講蒋荚,安卓第三個虛擬鍵所列出的那些就是任務(wù)。
下面的圖片顯示了向標(biāo)準(zhǔn)啟動模式的Activity分享照片時的情況馆蠕。雖然分別來自不同的應(yīng)用期升,但仍然它會和發(fā)送intent的Activity處于同一個任務(wù)中。
注:從圖中可以看出分享圖片的是Gallery應(yīng)用互躬。
同時你會看到此時任務(wù)管理器是這樣的(有一點(diǎn)怪異)播赁。
如果我們切換到另外一個應(yīng)用然后再切回到Gallery,你會發(fā)現(xiàn)standard launchMode啟動的Activity仍然在Gallery任務(wù)的上面吼渡,導(dǎo)致在操作Gallery之前容为,我們必須首先結(jié)束這個額外的Activity。
在Lollipop設(shè)備上的表現(xiàn)
如果Activity都是來自同一個應(yīng)用,其表現(xiàn)和Lollipop之前的設(shè)備一樣坎背,在任務(wù)的頂端替劈。
但是如果intent來自其他應(yīng)用,將創(chuàng)建一個新的任務(wù)得滤,同時新創(chuàng)建的Activity會被作為一個根Activity陨献,如下:
注:圖片中的Task#2和Task#3分別表示兩個任務(wù),序號大的比序號小的后啟動懂更。
下面是任務(wù)管理器中的樣子:
發(fā)生這種情況的原因是Lollipop中任務(wù)管理系統(tǒng)做了修改眨业,讓它看起來更合理了。因?yàn)樗鼈冊诓煌娜蝿?wù)中沮协,你可以直接切回Gallery龄捡,你還可以觸發(fā)另一個Intent,創(chuàng)建新的與之前相同的任務(wù)皂股。
撰寫郵件的Activity或者發(fā)布社交網(wǎng)絡(luò)狀態(tài)的Activity都是采用這種Activity的例子墅茉。如果你希望Activity單獨(dú)服務(wù)于一個Intent,就可以考慮standard啟動模式呜呐。
singleTop
接下來就是singleTop模式就斤。它的表現(xiàn)幾乎和standard模式一模一樣,一個singleTop Activity 的實(shí)例可以無限多蘑辑,唯一的區(qū)別是如果在棧頂已經(jīng)有一個相同類型的Activity實(shí)例洋机,Intent不會再創(chuàng)建一個Activity,而是通過onNewIntent()被發(fā)送到現(xiàn)有的Activity洋魂。
在singleTop模式下我們需要同時在onCreate() 和 onNewIntent()中處理發(fā)來的intent绷旗,以滿足不同情況。
這種啟動模式的用例之一就是搜索功能副砍。假設(shè)我們創(chuàng)建了一個搜索框衔肢,點(diǎn)擊搜索的時候?qū)?dǎo)航到一個顯示搜索結(jié)果列表的SearchActivity中,為了更好的用戶體驗(yàn)豁翎,這個搜索框一般也會被放到SearchActivity中角骤,這樣用戶想要再次搜索就不需要按返回鍵。
想像一下心剥,如果每次顯示搜索結(jié)果的時候我們都啟動一個新的activity邦尊,10次搜索10個activity,那樣當(dāng)我們想返回最初的那個activity的時候需要按10次返回优烧。
所以我們應(yīng)該這樣蝉揍,如果棧頂已經(jīng)有一個SearchActivity,我們將Intent發(fā)送給現(xiàn)有的activity畦娄,讓它來更新搜索結(jié)果又沾。這樣就只會有一個在棧頂?shù)腟earchActivity弊仪,只需點(diǎn)一次back就可以回到之前的activity。
不管怎樣捍掺,singleTop和它的調(diào)用者處在一個任務(wù)中撼短。如果你想要讓intent發(fā)送給另一個任務(wù)中處于棧頂?shù)腁ctivity,是不行的挺勿。
而當(dāng)Intent來自于另外一個應(yīng)用的時候,新的Activity的啟動方式和standard模式是一致的(pre-Lollipop:處于調(diào)用者任務(wù)的棧頂喂柒,Lollipop:會創(chuàng)建一個新的任務(wù))不瓶。
singleTask
這種模式和standard以及singleTop有很大不同。singleTask模式的Activity只允許在系統(tǒng)中有一個實(shí)例灾杰。如果系統(tǒng)中已經(jīng)有了一個實(shí)例蚊丐,持有這個實(shí)例的任務(wù)將移動到頂部,同時intent將被通過onNewIntent()發(fā)送艳吠。如果沒有麦备,則會創(chuàng)建一個新的Activity并置放在合適的任務(wù)中。
在同一個應(yīng)用中的情況
如果系統(tǒng)中還沒有singleTask的Activity昭娩,會新創(chuàng)建一個凛篙,并放在同一任務(wù)的棧頂。
但是如果已經(jīng)存在栏渺,singleTask Activity上面的所有Activity將以合適的方式自動銷毀呛梆,讓我們想要顯示的Activity處于棧頂。同時Intent也會通過onNewIntent()方法發(fā)送到這個singleTask Activity磕诊。
在用戶體驗(yàn)方面填物,可能不是很合理,但是它就是這樣設(shè)計的...
你可能注意到了官方文檔中提到的一個問題:
系統(tǒng)會創(chuàng)建一個新的任務(wù)霎终,并將這個Activity實(shí)例化為新任務(wù)的根部(root)滞磺。
但從實(shí)驗(yàn)結(jié)果來看,并不是這么回事莱褒。singleTask Activity仍然在任務(wù)的Activity棧頂击困,我們可以從dumpsys activity 命令顯示上看出來:
Taskid#239
TaskRecord{428efe30#239?A=com.thecheesefactory.lab.launchmode?U=0?sz=2}
Intent
{act=android.intent.action.MAIN?cat=[android.intent.category.LAUNCHER]
flg=0x10000000
cmp=com.thecheesefactory.lab.launchmode/.StandardActivity}
Hist#1:?ActivityRecord{429a88d0?u0?com.thecheesefactory.lab.launchmode/.SingleTaskActivity?t239}
Intent{cmp=com.thecheesefactory.lab.launchmode/.SingleTaskActivity}
ProcessRecord{4224313018965:com.thecheesefactory.lab.launchmode/u0a123}
Hist#0:?ActivityRecord{425fec98?u0?com.thecheesefactory.lab.launchmode/.StandardActivity?t239}
Intent
{act=android.intent.action.MAIN?cat=[android.intent.category.LAUNCHER]
flg=0x10000000
cmp=com.thecheesefactory.lab.launchmode/.StandardActivity}
ProcessRecord{4224313018965:com.thecheesefactory.lab.launchmode/u0a123}
如果你希望singleTask Activity表現(xiàn)的和文檔中描述的一致,你需要為singleTask Activity設(shè)置taskAffinity屬性保礼。
android:name=".SingleTaskActivity"
android:label="singleTask?launchMode"
android:launchMode="singleTask"
android:taskAffinity="">
這里是啟動SingleTaskActivity的的結(jié)果(使用了taskAffinity之后)沛励。
是否使用taskAffinity取決于你自己。
和其他應(yīng)用一起工作的情況
一旦intent是從另外的應(yīng)用發(fā)送過來炮障,并且系統(tǒng)中也沒有任何Activity的實(shí)例目派,則會創(chuàng)建一個新的任務(wù),并且新的Activity被作為根Activity創(chuàng)建胁赢。
除非擁有這個singleTask Activity 的應(yīng)用已經(jīng)存在企蹭,那樣的話,新建的Activity會置于這個任務(wù)的上面(而不是新建一個任務(wù))。
In case that there is an Activity instance existed?in any Task, the whole Task would be moved to top and every single Activity placed above the singleTask Activity will be destroyed with?lifecycle.If back button is pressed, user has to travel through the Activities in the stack before going back to the caller?Task.
假設(shè)已經(jīng)有了一個Activity的實(shí)例谅摄,不管它是在哪個任務(wù)中(包括上面的那種情況徒河,在用于這個Activity的應(yīng)用中),整個任務(wù)將被移到頂端送漠,而singleTask ?Activity上面的所有 Activity 都將被銷毀顽照, 用戶需要按back鍵遍歷玩棧中的Activity才能回到調(diào)用者任務(wù)。
這種模式的應(yīng)用案例有闽寡。郵件客戶端的收件箱或者社交網(wǎng)絡(luò)的時間軸代兵。這些Activity一般不會設(shè)計成擁有多個實(shí)例,singleTask可以滿足爷狈。但是在使用這種模式的時候必須要明智植影,因?yàn)橛行〢ctivity會在用戶不知情的情況下被銷毀。
singleInstance
這個模式非常接近于singleTask涎永,系統(tǒng)中只允許一個Activity的實(shí)例存在思币。區(qū)別在于持有這個Activity的任務(wù)中只能有一個Activity:即這個單例本身。If?another Activity is called from this kind of Activity, a new Task would be automatically created to place that new Activity. Likewise, if singleInstance Activity is called, new Task would be created to place the Activity.
不過結(jié)果卻很怪異羡微,從dumpsys提供的信息來看谷饿,似乎系統(tǒng)中有兩個任務(wù)但任務(wù)管理器中只顯示一個,即最后被移到頂部的那個。導(dǎo)致雖然后臺有一個任務(wù)在運(yùn)行拷淘,我們卻無法切換回去各墨,這一點(diǎn)也不科學(xué)。
下面是當(dāng)singleInstance Activity被調(diào)用的同時棧中已經(jīng)有一些Activity的情況下所發(fā)生的事情:
本來有兩個任務(wù)启涯,但是任務(wù)管理器中卻只顯示一個任務(wù):
Since this Task could has only one Activity, we couldn't switch back to Task #1 anymore. Only?way to do so is to relaunch the application from launcher but it appears that the singleInstance Task would be hidden in the background instead.
因?yàn)檫@個任務(wù)只有一個Activity贬堵,我們再也無法切回到任務(wù)#1了。唯一的辦法是重新在launcher中啟動這個應(yīng)用结洼。?but之后的沒有翻譯黎做,因?yàn)槲乙膊幻靼鬃髡叩囊馑肌?/p>
不過這個問題也有解決方案,就像我們在singleTask Acvity中做的松忍,只要為singleInstance Activity設(shè)置taskAffinity屬性就可以了蒸殿。
android:name=".SingleInstanceActivity"
android:label="singleInstance?launchMode"
android:launchMode="singleInstance"
android:taskAffinity="">
現(xiàn)在科學(xué)多了。
這種模式很少被使用鸣峭。實(shí)際使用的案例如Launcher的Activity或者100%確定只有一個Activity的應(yīng)用宏所。總之除非完全有必要摊溶,不然我不建議使用這種模式爬骤。
Intent Flags
除了在AndroidManifest.xml中直接設(shè)置launch mode,我們還可以通過叫做Intent Flags的東西設(shè)置更多的行為莫换,比如:
Intentintent=newIntent(StandardActivity.this,StandardActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
這段代碼將會啟動一個singleTop啟動模式的的StandardActivity霞玄。
有許多種Flag可以使用骤铃,更多的請參考Intent。
希望這篇文章對你有用坷剧。