2.2 Activity的啓動模式大全

點此進入:從零快速構建APP系列目錄導圖
點此進入:UI編程系列目錄導圖
點此進入:四大組件系列目錄導圖
點此進入:數據網絡和線程系列目錄導圖

1、啓動一個Activity的幾種方式

在Android中我們可以通過下面兩種方式來啓動一個新的Activity,注意這裏是怎麼啓動,而非 啓動模式!!分爲顯示啓動和隱式啓動!

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來實現 這個是通過Intent的intent-filter來實現的。
3. 另外還有一個直接通過包名啓動apk的:

Intent intent = getPackageManager().getLaunchIntentForPackage(“apk第一個啓動的Activity的全限定類名”) ;
if(intent != null) startActivity(intent) ;

2、Activity的管理

我們來看下官方文檔給出的一個流程圖:

流程解析:
應用程序中存在A1,A2,A3三個activity,當用戶在Launcher或Home Screen點擊應用程序圖標時, 啓動主A1,接着A1開啓A2,A2開啓A3,這時棧中有三個Activity,並且這三個Activity默認在 同一個任務(Task)中,當用戶按返回時,彈出A3,棧中只剩A1和A2,再按返回鍵, 彈出A2,棧中只剩A1,再繼續按返回鍵,彈出A1,任務被移除,即程序退出!

3、Task的管理

Task是Activity的集合,是一個概念,實際使用的Back Stack來存儲Activity,可以有多個Task,但是 同一時刻只有一個棧在最前面,其他的都在後臺!那棧是如何產生的呢?

答:當我們通過主屏幕,點擊圖標打開一個新的App,此時會創建一個新的Task!舉個例子:我們通過點擊通信錄APP的圖標打開APP,這個時候會新建一個棧1,然後開始把新產生的Activity添加進來,可能我們在通訊錄的APP中打開了短信APP的頁面,但是此時不會新建一個棧,而是繼續添加到棧1中,這是 Android推崇一種用戶體驗方式,即不同應用程序之間的切換能使用戶感覺就像是同一個應用程序, 很連貫的用戶體驗,官方稱其爲seamless (無縫銜接)! 這個時候假如我們點擊Home鍵,回到主屏幕,此時棧1進入後臺,我們可能有下述幾種操作:
- 點擊菜單鍵(正方形那個按鈕),點擊打開剛剛的程序,然後棧1又回到前臺了! 又或者我們點擊主屏幕上通信錄的圖標,打開APP,此時也不會創建新的棧,棧1回到前臺!
- 如果此時我們點擊另一個圖標打開一個新的APP,那麼此時則會創建一個新的棧2,棧2就會到前臺, 而棧1繼續呆在後臺;

後面也是這樣,以此類推!

如上面所述,Android會將新成功啓動的Activity添加到同一個Task中並且按照以”先進先出”方式管理多個Task 和Back Stack,用戶就無需去擔心Activites如何與Task任務進行交互又或者它們是如何存在於Back Stack中! 或許,你想改變這種正常的管理方式。比如,你希望你的某個Activity能夠在一個新的Task中進行管理; 或者你只想對某個Activity進行實例化,又或者你想在用戶離開任務時清理Task中除了根Activity所有Activities。你可以做這些事或者更多,只需要通過修改AndroidManifest.xml中 的相關屬性值或者在代碼中通過傳遞特殊標識的Intent給startActivity()就可以輕鬆的實現 對Actvitiy的管理了。

Activity是安卓上最聰明的設計之一,優秀的內存管理讓多任務完美運行在最流行的操作系統之上。並不是讓Activity在屏幕上啓動就完事了,其啓動方式也是需要關注的。這個話題的內容很多,其中很重要的就是啓動模式。

Activity的啓動模式對我們來說應該是個全新的概念,在實際項目中我們應該根據特定的需求爲每個Activity指定恰當的啓動模式。啓動模式一共有四種,分別是 standard、 singleTop、singleTask 和 singleInstance,可以在 AndroidManifest.xml 中通過給 標籤指定android:launchMode 屬性來選擇啓動模式,下面我們來逐個進行學習。

一、standard模式

standard 是Activity默認的啓動模式,在不進行顯式指定的情況下,所有Activity都會自動使用這種啓動模式。因此,到目前爲止我們寫過的所有Activity都是使用的 standard 模式。經過之前的學習,我們已經知道了 Android 是使用返回棧來管理活動的,在 standard 模式(即默認情況)下,每當啓動一個新的Activity,它就會在返回棧中入棧,並處於棧頂的位置。對於使用 standard 模式的活動,系統不會在乎這個Activity是否已經在返回棧中存在,每次啓動都會創建該Activity的一個新的實例。

也就是說在這種模式下啓動的Activity可以被多次實例化,即在同一個任務中可以存在多個Activity的實例,每個實例都會處理一個Intent對象。如果Activity A的啓動模式爲standard,並且A已經啓動,在A中再次啓動Activity A,即調用startActivity(new Intent(this,A.class)),會在A的上面再次啓動一個A的實例,即當前的桟中的狀態爲A –> A。

下面的圖片顯示了向標準啓動模式的Activity分享照片時的情況。雖然分別來自不同的應用,但仍然它會和發送intent的Activity處於同一個任務中。

在Lollipop設備上的表現:
如果Activity都是來自同一個應用,其表現和Lollipop之前的設備一樣,在任務的頂端:

但是如果intent來自其他應用,將創建一個新的任務,同時新創建的Activity會被作爲一個根Activity,如下:

下面是任務管理器中的樣子:

發生這種情況的原因是Lollipop中任務管理系統做了修改,讓它看起來更合理了。因爲它們在不同的任務中,你可以直接切回Gallery,你還可以觸發另一個Intent,創建新的與之前相同的任務。
standard6.jpg

撰寫郵件的Activity或者發佈社交網絡狀態的Activity都是採用這種Activity的例子。如果你希望Activity單獨服務於一個Intent,就可以考慮standard啓動模式。

二、singleTop模式

可能在有些情況下,你會覺得 standard 模式不太合理。Activity明明已經在棧頂了,爲什麼再次啓動的時候還要創建一個新的活動實例呢?彆着急,這只是系統默認的一種啓動模式而已,我們完全可以根據自己的需要進行修改,比如說使用 singleTop 模式。當Activity的啓動模式指定爲 singleTop,在啓動活動時如果發現返回棧的棧頂已經是該活動,則認爲可以直接使用它,不會創建新的實例,而是重用位於棧頂的那個實例, 並且會調用該實例的onNewIntent()方法將Intent對象傳遞到這個實例中。

舉例來說,如果A的啓動模式爲singleTop,並且A的一個實例已經存在於棧頂中, 那麼再調用startActivity(new Intent(this,A.class))啓動A時, 不會再次創建A的實例,而是重用原來的實例,並且調用原來實例的onNewIntent()方法。 這時任務棧中還是這有一個A的實例。如果以singleTop模式啓動的Activity的一個實例 已經存在與任務棧中,但是不在棧頂,那麼它的行爲和standard模式相同,也會創建多個實例。

三、singleTask模式

使用 singleTop 模式可以很好地解決重複創建棧頂活動的問題,但是正如上面所說的,如果該Activity並沒有處於棧頂的位置,還是可能會創建多個Activity實例的。那麼有沒有什麼辦法可以讓某個Activity在整個應用程序的上下文中只存在一個實例呢?這就要藉助singleTask 模式來實現了。

當Activity的啓動模式指定爲 singleTask,每次啓動該Activity時系統首先會在返回棧中檢查是否存在該Activity的實例,如果發現已經存在則直接使用該實例,並把在這個Activity之上的所有Activity統統出棧,如果沒有發現就會創建一個新的Activity實例。

但是如果已經存在,singleTask Activity上面的所有Activity將以合適的方式自動銷燬,讓我們想要顯示的Activity處於棧頂。同時Intent也會通過onNewIntent()方法發送到這個singleTask Activity。

官方文檔中提到的一個問題:系統會創建一個新的任務,並將這個Activity實例化爲新任務的根部(root) 這個則需要我們對taskAffinity進行設置了,使用taskAffinity後的結果:

和其他應用一起工作的情況:一旦intent是從另外的應用發送過來,並且系統中也沒有任何Activity的實例,則會創建一個新的任務,並且新的Activity被作爲根Activity創建。

如果這個singleTask Activity 的應用已經存在,那麼新建的Activity會置於這個任務的上面(而不是新建一個任務)。

假設已經有了一個Activity的實例,不管它是在哪個任務中(包括上面的那種情況,在用於這個Activity的應用中),整個任務將被移到頂端,而singleTask  Activity上面的所有 Activity 都將被銷燬, 用戶需要按back鍵遍歷完棧中的Activity才能回到調用者任務。

這種模式的應用案例有:郵件客戶端的收件箱或者社交網絡的時間軸。這些Activity一般不會設計成擁有多個實例,singleTask可以滿足。但是在使用這種模式的時候必須要明智,因爲有些Activity會在用戶不知情的情況下被銷燬。

四、singleInstance模式

singleInstance 模式應該算是四種啓動模式中最特殊也最複雜的一個了, 我們也需要多花點功夫來理解這個模式。不同於以上三種啓動模式,指定爲 singleInstance 模式的Activity會啓用一個新的返回棧來管理這個Activity(其實如果 singleTask 模式指定了不同的 taskAffinity,也會啓動一個新的返回棧)。那麼這樣做有什麼意義呢?

想象以下場景,假設我們的程序中有一個Activity是允許其他程序調用的,如果我們想實現其他程序和我們的程序可以共享這個Activity的實例,應該如何實現呢?使用前面三種啓動模式肯定是做不到的,因爲每個應用程序都會有自己的返回棧,同一個Activity在不同的返回棧中入棧時必然是創建了新的實例。而使用 singleInstance 模式就可以解決這個問題,在這種模式下會有一個單獨的返回棧來管理這個Activity,不管是哪個應用程序來訪問這個Activity,都共用的同一個返回棧,也就解決了共享Activity實例的問題。

還有一點需要注意:當我們再次啓動該Activity的實例時,會重用已存在的任務和實例,並且會調用這個實例的onNewIntent()方法,將Intent實例傳遞到該實例中。和singleTask相同,同一時刻在系統中只會存在一個這樣的Activity實例。

不過結果卻很怪異,從顯示結果來看,似乎系統中有兩個任務但任務管理器中只顯示一個,即最後被移到頂部的那個。導致雖然後臺有一個任務在運行,我們卻無法切換回去,這一點也不科學。

下面是當 singleInstance Activity 被調用的同時棧中已經有一些Activity的情況下所發生的事情:

本來有兩個任務,但是任務管理器中卻只顯示一個任務:
SingleInstance3.jpg

因爲這個任務只有一個Activity,我們再也無法切回到 任務#1 了,唯一的辦法是重新在launcher中啓動這個應用。

不過這個問題也有解決方案,就像我們在singleTask Acvity中做的,只要爲singleInstance Activity設置taskAffinity屬性就可以了:

<activity
            android:name=".SingleInstanceActivity"
            android:label="singleInstance launchMode"
            android:launchMode="singleInstance"
            android:taskAffinity="">

現在就變得正常多了:

這種模式很少被使用,實際使用的案例如Launcher的Activity或者100%確定只有一個Activity的應用。總之除非完全有必要,不然我不建議使用這種模式。

Intent Flags:

除了在AndroidManifest.xml中直接設置launch mode,我們還可以通過叫做 Intent Flags 的東西設置更多的行爲,比如:

Intent intent = new Intent(StandardActivity.this, StandardActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);

這段代碼將會啓動一個singleTop啓動模式的的StandardActivity 。

點此進入:GitHub開源項目“愛閱”。“愛閱”專注於收集優質的文章、站點、教程,與大家共分享。下面是“愛閱”的效果圖:


聯繫方式:

簡書:WillFlow
CSDN:WillFlow
微信公衆號:WillFlow

微信公衆號:WillFlow

發佈了81 篇原創文章 · 獲贊 21 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章