Android 圖文教學讓你徹底理解activity啟動模式

希爾瓦娜斯女神發表於2015-12-11

我們首先從最簡單的開始,

standard

這個模式就是預設的模式,我們都知道 當你用這個模式時,每次傳送一個intent,都會生成一個新的例項!

我寫一個簡單的例子:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 3     package="com.example.administrator.lanuchmodetest">
 4 
 5     <application
 6         android:allowBackup="true"
 7         android:icon="@mipmap/ic_launcher"
 8         android:label="@string/app_name"
 9         android:supportsRtl="true"
10         android:theme="@style/AppTheme">
11         <activity
12             android:name=".MainActivity"
13             android:label="@string/app_name"
14             android:theme="@style/AppTheme.NoActionBar">
15             <intent-filter>
16                 <action android:name="android.intent.action.MAIN" />
17 
18                 <category android:name="android.intent.category.LAUNCHER" />
19             </intent-filter>
20         </activity>
21         <activity
22             android:name=".TestMainActivity"
23             android:label="@string/title_activity_test_main"
24             android:theme="@style/AppTheme.NoActionBar"></activity>
25     </application>
26 
27 </manifest>

對於這個app來說,MainActivity 是他的第一個預設啟動的acitivity,TestMainActivity是第二個activity,MainActivity上有一個按鈕 點選以後就傳送intent會跳轉到TestMainActivity上,

我重寫了TestMainActivity的onBackPressed方法,使得在這個activity上按返回鍵時 不會走系統自動銷燬的方法,而是傳送intent到MainActivity上來啟動MainActivity。

 

當我們重複了幾次這個動作以後,在命令列上輸入 adb shell dumpsys activity activities ,可以得到:

 

 

你看 這個就能清晰的看出來 當前我們這個app 有一個task棧,這個棧裡所有存在的activity都在這個task裡,有很多重複的。

當然了這個是最簡單的情況,我們現在考慮 如果是外部一個app 來啟動我們這個app裡的TestMainActivity是什麼情況?

現在我就來寫一個outerlanuchmodetest 的app 來啟動我們lanuchmodetest 這個app裡的 TestMainActivity 看看是什麼效果

(這裡程式碼太簡單略了),寫完以後執行 命令


你會發現 TestMain雖然是定義在lanuchmode這個app裡的但是 如果外部app啟動了他,他還是會在那個外部app裡的task!

 

於此同時,在我這個低於5.0(4.3版本)的模擬器裡,你開啟最近啟動app的列表 你會發現是這樣的:

 

 

同時我們點進去以後 會發現 我們必須點一次返回鍵才能回到我們的Outer這個app自己的介面。

 

 

 

singleTop

這個稍微有點難度,比上面稍微複雜點。實際上singletop和standrd 基本上差不多,也是可以有多個activity例項的,

唯一的不同在於:

如果呼叫的 目標activity 位於呼叫者的task的 棧頂,則不會建立新的例項,而是使用當前的這個activity 並呼叫onNewIntent方法。

目標 activity 位於呼叫者的task的棧頂,很多人理解不了,其實翻譯過來就是自己啟動自己。比如很多app裡的搜尋結果介面

就是這樣的,你每次搜尋 其實都是呼叫onNewIntent。設想一下,如果不用這個singleTop,那你搜尋100次 不就有100個搜尋頁面了

那你要回到上個頁面 得返回100次。

下面來做一個app,這個app的testMainActivity頁面 有個按鈕 點選他就啟動自己,我們看看會發生什麼?

 

我們可以看出來,當第一次啟動這個testmain的時候 onNewIntent是不走的 走的是onCreate ,以後每傳送一個

啟動這個頁面的intent,onCreate都不會走了 都是走的onNewIntent。

現在看看task裡的情況:

當然你要是從另外一個app去啟動的話 ,因為另外一個app 有自己的task 所以當然是可以有2個testMainActivity例項的:

 

singleTask

這個相對於singetop來說,這個又要複雜一些。

用這個啟動模式的,activity 在整個系統中都只有1個!注意是整個系統 而不是某個task 這是和top最大的區別。

如果這個例項存在 那就會由onNEWINTENT來 接受這個intent。這一點倒是和top一樣。

並且 會把他前面的 activity全部銷燬掉。

 1  <activity
 2             android:name=".TestMainActivity"
 3             android:label="@string/title_activity_test_main"
 4             android:launchMode="singleTask"
 5             android:theme="@style/AppTheme.NoActionBar">
 6             <intent-filter>
 7                 <action android:name="android.intent.action.TEST_LANUCHMODE" />
 8 
 9                 <category android:name="android.intent.category.DEFAULT" />
10             </intent-filter>
11         </activity>

 

好,我們現在就來用圖的形式 來解釋一下這個過程:

我們假設main 點選以後跳轉到testmain 

testmain 點選以後 跳轉到main2

main2 點選以後 跳轉到main3

此時的task 情況為:

此時main3 點選一下 跳轉到testmain以後的task情況為:

很符合我們的預期對吧,但是 如果你稍微改一下:

 1  <activity
 2             android:name=".TestMainActivity"
 3             android:label="@string/title_activity_test_main"
 4             android:launchMode="singleTask"
 5             android:taskAffinity=""
 6             android:theme="@style/AppTheme.NoActionBar">
 7             <intent-filter>
 8                 <action android:name="android.intent.action.TEST_LANUCHMODE" />
 9 
10                 <category android:name="android.intent.category.DEFAULT" />
11             </intent-filter>
12         </activity>

你看 我們加了一個taskAffinity屬性,這個時候我們再重複一遍上述那個過程的第一步 你就會發現,此時的task情況是這樣的:

你看,這裡 我們發現 雖然都是一個應用內的activity互相啟動 但是當你加了這個屬性以後,我們由main 跳往testmain的時候

是新開的一個棧!而不是原來的main自己的那個棧了!當我們從main3 跳往testmain的時候:

 

依舊是如此! 所以要注意這個屬性 對singtask的影響!

包括你開啟工作管理員都是這樣的:

 

剛才我們講的是singtask的 應用內之間 的情況,現在來講一下 應用外跳轉的情況!

注意當跨應用跳轉時,taskAffinity 屬性就不影響singetask了。加不加都一樣(有興趣的同學可以自己試試)

當跨應用 跳轉時 情況如下:

 

1.當目標app程式不在的時候:

 

你看,此時我們還在活動的activity 就是系統的lanucher 和我們自己的outerlanuchmode。

此時如果啟動lanuchmode 這個app裡的testmain,那麼就會:

你看 此時系統task 就是這樣的,

 

 

所以針對這種情況 結論就是:當目標activity所屬的app沒有啟動,並且activity屬性屬於singetask的時候,

呼叫者 啟動這個acitivity的結果就是 必定會在另外一個task裡!

 

2.第二種情況:

目標activity所屬的app 已經啟動了,但是目標activity還沒有啟動

此時如果點選啟動目標activity

 

結果也很明瞭,當然還有另外一種複雜的情況,如果目標activity已經有了testmainactivity 存在 會怎麼樣,這裡就不寫了,

有興趣的同學可以自己寫個demo 看下task的情況。

 

singleInstance

最後看一下 這個單例模式,其實這個最簡單,就是無論什麼時候,這個屬性的activity 都是自己單獨佔據一個棧的。

並且和前面的singleTask類似,在系統中 只有一份!

比方說我們從main 跳轉到testmain 這個單例:

 1   <activity
 2             android:name=".TestMainActivity"
 3             android:label="@string/title_activity_test_main"
 4             android:launchMode="singleInstance"
 5             android:theme="@style/AppTheme.NoActionBar">
 6             <intent-filter>
 7                 <action android:name="android.intent.action.TEST_LANUCHMODE" />
 8 
 9                 <category android:name="android.intent.category.DEFAULT" />
10             </intent-filter>
11         </activity>

 

看上去 是和singletask 一樣,但是如果你開啟工作管理員 你會發現:

 

 

只有一個!

並且當你從工作管理員 進入到這個介面時,你點返回 是無法回到main的!

所以說這種情況 雖然是2個task ,但是系統在工作管理員只顯示一個!

並且返回回不到前面一個acitivity,這樣的體驗非常糟糕 一定要慎用!

當然解決方法也有,只要加上taskaffinity屬性即可!

加上以後 你再開啟工作管理員 就是:

一切正常。

不過還是想多說一句 單例模式 儘量慎用,尤其是不帶taskaffinity屬性的,據我目前看過的原始碼裡面 應該只有

某些手機rom裡的lanucher 會用這個屬性,其他任何app中 我都沒有見過用這個屬性的。所以大家也一定要慎用!

因為會給使用者造成困惑!

 

相關文章