1搞监、啟動一個Activity的幾種方式
在Android中我們可以通過下面兩種方式來啟動一個新的Activity,注意這里是怎么啟動璃哟,而非 啟動模式L仁恰黄虱!分為顯示啟動和隱式啟動!
1. 顯式啟動:通過包名來啟動白热,寫法如下:
①最常見的:
startActivity(new Intent(當前Act.this,要啟動的Act.class));
②通過Intent的ComponentName:ComponentName cn = new ComponentName("當前Act的全限定類名","啟動Act的全限定類名") ;Intent intent = new Intent() ;intent.setComponent(cn) ;startActivity(intent) ;
③初始化Intent時指定包名:Intent intent = new Intent("android.intent.action.MAIN");intent.setClassName("當前Act的全限定類名","啟動Act的全限定類名");startActivity(intent);
2.隱式啟動:通過Intent-filter的Action,Category或data來實現(xiàn) 這個是通過Intent的intent-filter來實現(xiàn)的敛助。
3. 另外還有一個直接通過包名啟動apk的:
Intent intent = getPackageManager().getLaunchIntentForPackage("apk第一個啟動的Activity的全限定類名") ;
if(intent != null) startActivity(intent) ;
2、Activity的管理
我們來看下官方文檔給出的一個流程圖:
流程解析:
應(yīng)用程序中存在A1,A2,A3三個activity屋确,當用戶在Launcher或Home Screen點擊應(yīng)用程序圖標時纳击, 啟動主A1续扔,接著A1開啟A2,A2開啟A3评疗,這時棧中有三個Activity,并且這三個Activity默認在 同一個任務(wù)(Task)中茵烈,當用戶按返回時百匆,彈出A3,棧中只剩A1和A2呜投,再按返回鍵加匈, 彈出A2,棧中只剩A1仑荐,再繼續(xù)按返回鍵雕拼,彈出A1,任務(wù)被移除粘招,即程序退出啥寇!
3、Task的管理
Task是Activity的集合洒扎,是一個概念辑甜,實際使用的Back Stack來存儲Activity,可以有多個Task袍冷,但是 同一時刻只有一個棧在最前面磷醋,其他的都在后臺!那棧是如何產(chǎn)生的呢胡诗?
答:當我們通過主屏幕邓线,點擊圖標打開一個新的App,此時會創(chuàng)建一個新的Task煌恢!舉個例子:我們通過點擊通信錄APP的圖標打開APP骇陈,這個時候會新建一個棧1,然后開始把新產(chǎn)生的Activity添加進來瑰抵,可能我們在通訊錄的APP中打開了短信APP的頁面缩歪,但是此時不會新建一個棧,而是繼續(xù)添加到棧1中谍憔,這是 Android推崇一種用戶體驗方式匪蝙,即不同應(yīng)用程序之間的切換能使用戶感覺就像是同一個應(yīng)用程序, 很連貫的用戶體驗习贫,官方稱其為seamless (無縫銜接)逛球! 這個時候假如我們點擊Home鍵,回到主屏幕苫昌,此時棧1進入后臺颤绕,我們可能有下述幾種操作:
- 點擊菜單鍵(正方形那個按鈕),點擊打開剛剛的程序,然后棧1又回到前臺了奥务! 又或者我們點擊主屏幕上通信錄的圖標物独,打開APP,此時也不會創(chuàng)建新的棧氯葬,棧1回到前臺挡篓!
- 如果此時我們點擊另一個圖標打開一個新的APP,那么此時則會創(chuàng)建一個新的棧2帚称,棧2就會到前臺官研, 而棧1繼續(xù)呆在后臺;
后面也是這樣闯睹,以此類推戏羽!
如上面所述,Android會將新成功啟動的Activity添加到同一個Task中并且按照以"先進先出"方式管理多個Task 和Back Stack楼吃,用戶就無需去擔心Activites如何與Task任務(wù)進行交互又或者它們是如何存在于Back Stack中始花! 或許,你想改變這種正常的管理方式孩锡。比如衙荐,你希望你的某個Activity能夠在一個新的Task中進行管理; 或者你只想對某個Activity進行實例化浮创,又或者你想在用戶離開任務(wù)時清理Task中除了根Activity所有Activities忧吟。你可以做這些事或者更多,只需要通過修改AndroidManifest.xml中 <activity>的相關(guān)屬性值或者在代碼中通過傳遞特殊標識的Intent給startActivity()就可以輕松的實現(xiàn) 對Actvitiy的管理了斩披。
Activity是安卓上最聰明的設(shè)計之一溜族,優(yōu)秀的內(nèi)存管理讓多任務(wù)完美運行在最流行的操作系統(tǒng)之上。并不是讓Activity在屏幕上啟動就完事了垦沉,其啟動方式也是需要關(guān)注的煌抒。這個話題的內(nèi)容很多,其中很重要的就是啟動模式厕倍。
Activity的啟動模式對我們來說應(yīng)該是個全新的概念寡壮,在實際項目中我們應(yīng)該根據(jù)特定的需求為每個Activity指定恰當?shù)膯幽J健幽J揭还灿兴姆N讹弯,分別是 standard况既、 singleTop、singleTask 和 singleInstance组民,可以在 AndroidManifest.xml 中通過給 <activity>標簽指定android:launchMode 屬性來選擇啟動模式棒仍,下面我們來逐個進行學(xué)習(xí)。
一臭胜、standard模式
standard 是Activity默認的啟動模式莫其,在不進行顯式指定的情況下癞尚,所有Activity都會自動使用這種啟動模式。因此乱陡,到目前為止我們寫過的所有Activity都是使用的 standard 模式浇揩。經(jīng)過之前的學(xué)習(xí),我們已經(jīng)知道了 Android 是使用返回棧來管理活動的憨颠,在 standard 模式(即默認情況)下胳徽,每當啟動一個新的Activity,它就會在返回棧中入棧烙心,并處于棧頂?shù)奈恢媚だ取τ谑褂?standard 模式的活動乏沸,系統(tǒng)不會在乎這個Activity是否已經(jīng)在返回棧中存在淫茵,每次啟動都會創(chuàng)建該Activity的一個新的實例。
也就是說在這種模式下啟動的Activity可以被多次實例化蹬跃,即在同一個任務(wù)中可以存在多個Activity的實例匙瘪,每個實例都會處理一個Intent對象。如果Activity A的啟動模式為standard蝶缀,并且A已經(jīng)啟動丹喻,在A中再次啟動Activity A,即調(diào)用startActivity(new Intent(this翁都,A.class))碍论,會在A的上面再次啟動一個A的實例,即當前的桟中的狀態(tài)為A --> A柄慰。
下面的圖片顯示了向標準啟動模式的Activity分享照片時的情況鳍悠。雖然分別來自不同的應(yīng)用,但仍然它會和發(fā)送intent的Activity處于同一個任務(wù)中坐搔。
在Lollipop設(shè)備上的表現(xiàn):
如果Activity都是來自同一個應(yīng)用藏研,其表現(xiàn)和Lollipop之前的設(shè)備一樣,在任務(wù)的頂端:
但是如果intent來自其他應(yīng)用概行,將創(chuàng)建一個新的任務(wù)蠢挡,同時新創(chuàng)建的Activity會被作為一個根Activity,如下:
下面是任務(wù)管理器中的樣子:
發(fā)生這種情況的原因是Lollipop中任務(wù)管理系統(tǒng)做了修改凳忙,讓它看起來更合理了业踏。因為它們在不同的任務(wù)中,你可以直接切回Gallery涧卵,你還可以觸發(fā)另一個Intent堡称,創(chuàng)建新的與之前相同的任務(wù)。
撰寫郵件的Activity或者發(fā)布社交網(wǎng)絡(luò)狀態(tài)的Activity都是采用這種Activity的例子艺演。如果你希望Activity單獨服務(wù)于一個Intent却紧,就可以考慮standard啟動模式桐臊。
二、singleTop模式
可能在有些情況下晓殊,你會覺得 standard 模式不太合理断凶。Activity明明已經(jīng)在棧頂了,為什么再次啟動的時候還要創(chuàng)建一個新的活動實例呢巫俺?別著急认烁,這只是系統(tǒng)默認的一種啟動模式而已,我們完全可以根據(jù)自己的需要進行修改介汹,比如說使用 singleTop 模式却嗡。當Activity的啟動模式指定為 singleTop,在啟動活動時如果發(fā)現(xiàn)返回棧的棧頂已經(jīng)是該活動嘹承,則認為可以直接使用它窗价,不會創(chuàng)建新的實例,而是重用位于棧頂?shù)哪莻€實例叹卷, 并且會調(diào)用該實例的onNewIntent()方法將Intent對象傳遞到這個實例中撼港。
舉例來說,如果A的啟動模式為singleTop骤竹,并且A的一個實例已經(jīng)存在于棧頂中帝牡, 那么再調(diào)用startActivity(new Intent(this,A.class))啟動A時蒙揣, 不會再次創(chuàng)建A的實例靶溜,而是重用原來的實例,并且調(diào)用原來實例的onNewIntent()方法懒震。 這時任務(wù)棧中還是這有一個A的實例罩息。如果以singleTop模式啟動的Activity的一個實例 已經(jīng)存在與任務(wù)棧中,但是不在棧頂挎狸,那么它的行為和standard模式相同扣汪,也會創(chuàng)建多個實例。
三锨匆、singleTask模式
使用 singleTop 模式可以很好地解決重復(fù)創(chuàng)建棧頂活動的問題崭别,但是正如上面所說的,如果該Activity并沒有處于棧頂?shù)奈恢每致啵€是可能會創(chuàng)建多個Activity實例的茅主。那么有沒有什么辦法可以讓某個Activity在整個應(yīng)用程序的上下文中只存在一個實例呢?這就要借助singleTask 模式來實現(xiàn)了土榴。
當Activity的啟動模式指定為 singleTask诀姚,每次啟動該Activity時系統(tǒng)首先會在返回棧中檢查是否存在該Activity的實例,如果發(fā)現(xiàn)已經(jīng)存在則直接使用該實例玷禽,并把在這個Activity之上的所有Activity統(tǒng)統(tǒng)出棧赫段,如果沒有發(fā)現(xiàn)就會創(chuàng)建一個新的Activity實例呀打。
但是如果已經(jīng)存在,singleTask Activity上面的所有Activity將以合適的方式自動銷毀糯笙,讓我們想要顯示的Activity處于棧頂贬丛。同時Intent也會通過onNewIntent()方法發(fā)送到這個singleTask Activity。
官方文檔中提到的一個問題:系統(tǒng)會創(chuàng)建一個新的任務(wù)给涕,并將這個Activity實例化為新任務(wù)的根部(root) 這個則需要我們對taskAffinity進行設(shè)置了豺憔,使用taskAffinity后的結(jié)果:
和其他應(yīng)用一起工作的情況:一旦intent是從另外的應(yīng)用發(fā)送過來,并且系統(tǒng)中也沒有任何Activity的實例够庙,則會創(chuàng)建一個新的任務(wù)恭应,并且新的Activity被作為根Activity創(chuàng)建。
如果這個singleTask Activity 的應(yīng)用已經(jīng)存在耘眨,那么新建的Activity會置于這個任務(wù)的上面(而不是新建一個任務(wù))昼榛。
假設(shè)已經(jīng)有了一個Activity的實例,不管它是在哪個任務(wù)中(包括上面的那種情況毅桃,在用于這個Activity的應(yīng)用中)褒纲,整個任務(wù)將被移到頂端准夷,而singleTask Activity上面的所有 Activity 都將被銷毀钥飞, 用戶需要按back鍵遍歷完棧中的Activity才能回到調(diào)用者任務(wù)。
這種模式的應(yīng)用案例有:郵件客戶端的收件箱或者社交網(wǎng)絡(luò)的時間軸衫嵌。這些Activity一般不會設(shè)計成擁有多個實例读宙,singleTask可以滿足。但是在使用這種模式的時候必須要明智楔绞,因為有些Activity會在用戶不知情的情況下被銷毀结闸。
四、singleInstance模式
singleInstance 模式應(yīng)該算是四種啟動模式中最特殊也最復(fù)雜的一個了酒朵, 我們也需要多花點功夫來理解這個模式桦锄。不同于以上三種啟動模式,指定為 singleInstance 模式的Activity會啟用一個新的返回棧來管理這個Activity(其實如果 singleTask 模式指定了不同的 taskAffinity蔫耽,也會啟動一個新的返回棧)结耀。那么這樣做有什么意義呢?
想象以下場景匙铡,假設(shè)我們的程序中有一個Activity是允許其他程序調(diào)用的图甜,如果我們想實現(xiàn)其他程序和我們的程序可以共享這個Activity的實例,應(yīng)該如何實現(xiàn)呢鳖眼?使用前面三種啟動模式肯定是做不到的黑毅,因為每個應(yīng)用程序都會有自己的返回棧,同一個Activity在不同的返回棧中入棧時必然是創(chuàng)建了新的實例钦讳。而使用 singleInstance 模式就可以解決這個問題矿瘦,在這種模式下會有一個單獨的返回棧來管理這個Activity枕面,不管是哪個應(yīng)用程序來訪問這個Activity,都共用的同一個返回棧缚去,也就解決了共享Activity實例的問題膊畴。
還有一點需要注意:當我們再次啟動該Activity的實例時,會重用已存在的任務(wù)和實例病游,并且會調(diào)用這個實例的onNewIntent()方法唇跨,將Intent實例傳遞到該實例中。和singleTask相同衬衬,同一時刻在系統(tǒng)中只會存在一個這樣的Activity實例买猖。
不過結(jié)果卻很怪異,從顯示結(jié)果來看滋尉,似乎系統(tǒng)中有兩個任務(wù)但任務(wù)管理器中只顯示一個玉控,即最后被移到頂部的那個。導(dǎo)致雖然后臺有一個任務(wù)在運行狮惜,我們卻無法切換回去高诺,這一點也不科學(xué)。
下面是當 singleInstance Activity 被調(diào)用的同時棧中已經(jīng)有一些Activity的情況下所發(fā)生的事情:
本來有兩個任務(wù)碾篡,但是任務(wù)管理器中卻只顯示一個任務(wù):
因為這個任務(wù)只有一個Activity虱而,我們再也無法切回到 任務(wù)#1 了,唯一的辦法是重新在launcher中啟動這個應(yīng)用开泽。
不過這個問題也有解決方案牡拇,就像我們在singleTask Acvity中做的,只要為singleInstance Activity設(shè)置taskAffinity屬性就可以了:
<activity
android:name=".SingleInstanceActivity"
android:label="singleInstance launchMode"
android:launchMode="singleInstance"
android:taskAffinity="">
現(xiàn)在就變得正常多了:
這種模式很少被使用穆律,實際使用的案例如Launcher的Activity或者100%確定只有一個Activity的應(yīng)用惠呼。總之除非完全有必要峦耘,不然我不建議使用這種模式剔蹋。
Intent Flags:
除了在AndroidManifest.xml中直接設(shè)置launch mode,我們還可以通過叫做 Intent Flags 的東西設(shè)置更多的行為辅髓,比如:
Intent intent = new Intent(StandardActivity.this, StandardActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(intent);
這段代碼將會啟動一個singleTop啟動模式的的StandardActivity 泣崩。
點此進入:GitHub開源項目“愛閱”。
感謝優(yōu)秀的你跋山涉水看到了這里利朵,歡迎關(guān)注下讓我們永遠在一起律想!