taskAffinity 屬性詳解


一、本文目的

本篇文章的目的是為了 搞清楚,哪些情況下開啟一個 Activity 會在新的 task 運行,哪些情況下會繼續在原來的task 運行。

二、相關基礎知識

知識點 1

每個 Activity 運行時都有一個其歸屬的 task棧,我們可以用 activity.getTaskId() 的方法得到當前 activity 的taskId。如果兩個 activity 的 taskId 不同,則他們肯定不會屬於同一個 task。

為了方便,我們在 Application 中注冊生命周期回調,類似這樣,我們打印出當前 activity 和其歸屬的 taskid。

public class MyApplication extends Application { private static final String TAG_APP = MyApplication.class.getSimpleName(); @Override public void onCreate() { super.onCreate(); Log.d(TAG_APP,"MyApplication#onCreate"+Thread.currentThread().getName(); registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { //已過濾無用代碼,只在onresume 讀取相關數據 @Override public void onActivityResumed(Activity activity) { Log.d(TAG_APP,"onActivityResumed+"+activity.getClass().getSimpleName()+"####taskid = "+activity.getTaskId()); } }); } } 

知識點 2

taskAffinity 的使用方式如下,


image.png

如上圖所示,taskaffinity 可以單獨對一個 activity 使用,代表該 activity 所想歸屬的 task;
也能對application 使用,代表該 application 內聲明的所有 activity 都歸屬於這個task。

如果 activity 組件沒有聲明 taskAffinity 的話,該 activity 的 taskAffinity 屬性也是有默認值的。如果 application 指定了 taskAffinity 值,默認值就是 application 指定的 taskAffinity 值;如果 application 未指定的話,默認值就是 manifest 中聲明的包名(package 對應的字符串)。

知識點 3

Android 手機的任務列表就是根據不同 task 彈出的,我們可以根據任務管理器有幾個 item 圖標,來知道我們開啟了幾個 task。

知識點 4

是不是我指定了一個 Activity 的 taskAffinity 值(跟包名不同),運行該 Activity 時,是否就會新開這個 task棧呢?

答案是否定的,一個 Activity 運行時所歸屬的task,默認是啟動它 的那個Activity 所在的 task(下文將會驗證)。

知識點 5

taskAffinity 單獨使用並不會生效。
要想其生效,需要配合其他屬性使用,或者配合 Intent.FLAG_ACTIVITY_NEW_TASK,或者配合
allowTaskReparenting 。使用時用其中的一個就行,下面將詳細介紹這兩個屬性。

三、Intent.FLAG_ACTIVITY_NEW_TASK

Intent.FLAG_ACTIVITY_NEW_TASK 使用方式如下,

Intent intent = new Intent(this,IntentTimeActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); 

3.1 FLAG_ACTIVITY_NEW_TASK 解析規則

首先說下這個規則是根據測試結果反推出來的,不對之處還還請指出(捂臉。。。)

==當 AMS 發現啟動了一個帶有 FLAG_ACTIVITY_NEW_TASK 標簽的 Activity 時,會先去尋找當前是否存在這個 Activity 的 task 值(這個值具體是什么可看 知識點2),如果不存在的話,就會創建該task,如果存在就省去了創建 task 這個步驟。然后在把要啟動的 Activity 添加到 task 中。

3.2 測試case和結果

下面開始我們的測試,測試結果為過濾后的log日志,並給出相應分析。


我們假定都是 Activity A 跳轉到 Activity B 中,A沒有指定 taskAffinity 屬性,B 的launchMode 為standard。

case1: A、B 屬同一App, intent 未指定 FLAG_ACTIVITY_NEW_TASK,B 未指定 taskAffinity 屬性
D/MyApplication: onActivityResumed+MainActivity####taskid = 61 D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 61 

可以驗證:一個Activity 歸屬的task 是由 啟動它的 Activity 所決定的。

case2: A、B 屬同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK,B 指定 taskAffinity 屬性,但與包名相同
D/MyApplication: onActivityResumed+MainActivity####taskid = 62 D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 62 

可以驗證,一個 Activity 的默認 task 值就是 manifest 定義的包名。

case3: A、B 屬同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK,B 指定 taskAffinity 屬性,但與包名不同
D/MyApplication: onActivityResumed+MainActivity####taskid = 63 D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 63 

可以驗證:不指定 FLAG_ACTIVITY_NEW_TASK的話, 即使 taskAffinity 不同,一個Activity 歸屬的task 仍然是由 啟動它的 Activity 所決定的。

case4: A、B 屬同一App,intent 指定 FLAG_ACTIVITY_NEW_TASK,B 未指定 taskAffinity 屬性
D/MyApplication: onActivityResumed+MainActivity####taskid = 64 D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 64 

可以驗證:即使 使用了 FLAG_ACTIVITY_NEW_TASK,但由於兩者的 taskAffinity 相同,所以仍然不會開啟一個新的task。

case5: A、B 屬同一App,intent 指定 FLAG_ACTIVITY_NEW_TASK,B 指定 taskAffinity 屬性,且和包名不同
D/MyApplication: onActivityResumed+MainActivity####taskid = 65 D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 66 

可以驗證:開啟一個新task 的條件是 FLAG_ACTIVITY_NEW_TASK 和 taskAffinity 不同 缺一不可。


case6: A、B 屬同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK,B 未指定 taskAffinity 屬性,B啟動模式為 singletask or singletop
D/MyApplication: onActivityResumed+MainActivity####taskid = 67 D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 67 

可以得出:未指定 FLAG_ACTIVITY_NEW_TASK 和 新的 taskAffinity 時,這兩種啟動模式對task 沒有影響

case7: A、B 屬同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK,B 未指定 taskAffinity 屬性,B啟動模式為 singleinstance
D/MyApplication: onActivityResumed+MainActivity####taskid = 70 D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 71 

可以得出:
singleinstance 啟動模式本身就是會開啟一個新的task 裝載 這個Activity,且task 中只有這一個 Activity。
經判斷得知,AMS 是先對 launchMode 做判斷 再處理 FLAG_ACTIVITY_NEW_TASK 的,如果是 singleinstance ,則會直接開啟一個task。


上面七個 case 均是在同一個app 內的,現在考慮跨進程調用的情況,A在 app1,B在app2,此時 B 的默認 task 肯定是和 A 不同的
我們可以通過隱式啟動的方式啟動B,B 仍然是標准啟動模式。
類似這樣

//App1中 在Activity A 中我們這樣定義跳轉方法 public void onClickApp2(View v){ Intent intent = new Intent("xxx.lzq");//action 自己隨便定義就行,但要保證 跟 B 的 intent-filter 是相同的。 intent.addCategory(Intent.CATEGORY_DEFAULT); startActivity(intent); } //app2 中 manifest文件這樣定義 <activity android:name=".Main2Activity"> <intent-filter> <action android:name="xxx.lzq"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> 

++下文 所有的 App1的log標識為 MyApplication,App2 的標識為 MyApplication2。++

case 8: intent 未指定 FLAG_ACTIVITY_NEW_TASK
D/MyApplication: onActivityResumed+MainActivity####taskid = 74 D/MyApplication2: onActivityResumed+Main2Activity####taskid = 74 

可以得出:case 8 與 case 3 的本質是相同的,僅僅是 A 和 B 的taskA 屬性不同,所以沒有開啟新的task。
我們還可以看出,task 是可以跨進程的,即一個 task 中的 Activities 是可以運行在不同的進程中的。(關於 A 和 B 不在同一個進程讀者可自行驗證)

case 9: intent 指定 FLAG_ACTIVITY_NEW_TASK
D/MyApplication: onActivityResumed+MainActivity####taskid = 76 D/MyApplication2: onActivityResumed+Main2Activity####taskid = 77 

可以得出:case 9 與 case 5 的本質是相同的。

至此,Intent 的 FLAG_ACTIVITY_NEW_TASK 屬性 應該算是講解清楚了。

我們還可以得出一個有意思結論,那就是 AMS 分配的taskid 是線性遞增的,每次開啟一個新的task ,taskid 永遠都是 +1 的操作。

四、allowTaskReparenting 相關

測試該屬性的話,應該先把 FLAG_ACTIVITY_NEW_TASK 屬性去掉。

allowTaskReparenting 這個屬性指的是一個 Activity 運行時,可以重新選擇自己所屬的task。基本是在跨app 間調用時,我們在上面的case 8 的基礎上,對 Activity 做如下修改

// 將 allowTaskReparenting 設置為 true
<activity android:name=".Main2Activity" android:allowTaskReparenting="true"> <intent-filter> <action android:name="xxx.lzq"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </activity> 
case 10: taskReparenting 的時機:

當 A 啟動 B 時,這時雖然是在兩個進程中的,但其歸屬的task 是同一個,這時我們回到后台,在桌面點擊 B 的應用圖標,我們會發現 log 日志如下:
其中 MyApplication 代表 app1,MyApplication2代表 app2。

D/MyApplication: onActivityResumed+MainActivity####taskid = 83 D/MyApplication2: onActivityResumed+Main2Activity####taskid = 83 D/MyApplication2: onActivityResumed+Main2Activity####taskid = 84 
case 11: 假如 app2 之前啟動過,即app2 所歸屬的task 已經創建,這時我們再完全重復以上步驟,

log 日志如下:

D/MyApplication2: onActivityResumed+MainActivity####taskid = 86 D/MyApplication: onActivityResumed+MainActivity####taskid = 87 D/MyApplication2: onActivityResumed+Main2Activity####taskid = 87 D/MyApplication2: onActivityResumed+Main2Activity####taskid = 86 

此 case 正好驗證之前的解析規則,若 Activity taskAffinity指定的task 已經存在,是會復用之前的task,而不會再重新創建一個新的task。



作者:hayabusa_l
鏈接:https://www.jianshu.com/p/41dd76c3463d
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM