Activity任務棧

一、Activity任務棧的存在形態

通過命令adb shell dumpsys activity activities查看存在的任務棧信息如下:

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
Stack #30:
Task id #338
* TaskRecord{57d640 #338 A=com.didi.taskstack U=0 sz=4}
userId=0 effectiveUid=u0a113 mCallingUid=2000 mCallingPackage=null
affinity=com.didi.taskstack
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.didi.taskstack/.MainActivity}
realActivity=com.didi.taskstack/.MainActivity
autoRemoveRecents=false isPersistable=true numFullscreen=4 taskType=0 mTaskToReturnTo=0
rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
Activities=[ActivityRecord{50bedc3 u0 com.didi.taskstack/.MainActivity t338}, ActivityRecord{489b3da u0 com.didi.taskstack/.StandardActivity t338}, ActivityRecord{8b6c160 u0 com.didi.taskstack/.MainActivity t338}, ActivityRecord{3d149db u0 com.android.contacts/.activities.ContactSelectionActivity t338}]
askedCompatMode=false inRecents=true isAvailable=true
lastThumbnail=null lastThumbnailFile=/data/system/recent_images/338_task_thumbnail.png
stackId=30
hasBeenVisible=true mResizeable=false firstActiveTime=1472561978634 lastActiveTime=1472561978634 (inactive for 5s)
otherMwTaskId=0
* Hist #3: ActivityRecord{3d149db u0 com.android.contacts/.activities.ContactSelectionActivity t338}
packageName=com.android.contacts processName=com.android.contacts
launchedFromUid=10113 launchedFromPackage=com.didi.taskstack userId=0
app=ProcessRecord{8f9ee9f 27008:com.android.contacts/u0a3}
Intent { act=android.intent.action.PICK dat=content://contacts/people cmp=com.android.contacts/.activities.ContactSelectionActivity }
frontOfTask=false task=TaskRecord{57d640 #338 A=com.didi.taskstack U=0 sz=4}
taskAffinity=android.task.contacts
realActivity=com.android.contacts/.activities.ContactSelectionActivity
baseDir=/system/priv-app/Contacts/Contacts.apk
dataDir=/data/user/0/com.android.contacts
stateNotNeeded=false componentSpecified=false mActivityType=0
compat={480dpi} labelRes=0x7f0b0107 icon=0x7f030000 theme=0x2060008
config={1.0 ?mcc?mnc zh_CN ldltr sw360dp w360dp h580dp 480dpi nrml port finger -keyb/v/h -nav/h suim:1 s.10}
stackConfigOverride={1.0 ?mcc?mnc ?locale ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?}
taskDescription: iconFilename=null label=”null” color=fff0f0f0
launchFailed=false launchCount=1 lastLaunchTime=-5s422ms
haveState=false icicle=null
state=RESUMED stopped=false delayedResume=false finishing=false
keysPaused=false inHistory=true visible=true sleeping=false idle=true
fullscreen=true noDisplay=false immersive=false launchMode=1
frozenBeforeDestroy=false forceNewConfig=false
mActivityType=APPLICATION_ACTIVITY_TYPE
waitingVisible=false nowVisible=true lastVisibleTime=-4s890ms
* Hist #2: ActivityRecord{8b6c160 u0 com.didi.taskstack/.MainActivity t338}
packageName=com.didi.taskstack processName=com.didi.taskstack
launchedFromUid=10113 launchedFromPackage=com.didi.taskstack userId=0
app=ProcessRecord{362d717 29425:com.didi.taskstack/u0a113}
Intent { flg=0x10400000 cmp=com.didi.taskstack/.MainActivity }
frontOfTask=false task=TaskRecord{57d640 #338 A=com.didi.taskstack U=0 sz=4}
taskAffinity=com.didi.taskstack
realActivity=com.didi.taskstack/.MainActivity
baseDir=/data/app/com.didi.taskstack-1/base.apk
dataDir=/data/user/0/com.didi.taskstack
stateNotNeeded=false componentSpecified=true mActivityType=0
compat={480dpi} labelRes=0x7f060015 icon=0x7f030000 theme=0x7f090037
config={1.0 ?mcc?mnc zh_CN ldltr sw360dp w360dp h580dp 480dpi nrml port finger -keyb/v/h -nav/h suim:1 s.10}
stackConfigOverride={1.0 ?mcc?mnc ?locale ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?}
taskDescription: iconFilename=null label=”null” color=ff3f51b5
launchFailed=false launchCount=0 lastLaunchTime=-8s367ms
haveState=true icicle=Bundle[mParcelledData.dataSize=620]
state=STOPPED stopped=true delayedResume=false finishing=false
keysPaused=false inHistory=true visible=false sleeping=false idle=true
fullscreen=true noDisplay=false immersive=false launchMode=0
frozenBeforeDestroy=false forceNewConfig=false
mActivityType=APPLICATION_ACTIVITY_TYPE
waitingVisible=false nowVisible=false lastVisibleTime=-7s999ms
* Hist #1: ActivityRecord{489b3da u0 com.didi.taskstack/.StandardActivity t338}
packageName=com.didi.taskstack processName=com.didi.taskstack
launchedFromUid=10113 launchedFromPackage=com.didi.taskstack userId=0
app=ProcessRecord{362d717 29425:com.didi.taskstack/u0a113}
Intent { cmp=com.didi.taskstack/.StandardActivity }
frontOfTask=false task=TaskRecord{57d640 #338 A=com.didi.taskstack U=0 sz=4}
taskAffinity=com.didi.taskstack
realActivity=com.didi.taskstack/.StandardActivity
baseDir=/data/app/com.didi.taskstack-1/base.apk
dataDir=/data/user/0/com.didi.taskstack
stateNotNeeded=false componentSpecified=true mActivityType=0
compat={480dpi} labelRes=0x7f060015 icon=0x7f030000 theme=0x7f09008d
config={1.0 ?mcc?mnc zh_CN ldltr sw360dp w360dp h580dp 480dpi nrml port finger -keyb/v/h -nav/h suim:1 s.10}
stackConfigOverride={1.0 ?mcc?mnc ?locale ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?}
taskDescription: iconFilename=null label=”null” color=ff3f51b5
launchFailed=false launchCount=0 lastLaunchTime=-16s449ms
haveState=true icicle=Bundle[mParcelledData.dataSize=620]
state=STOPPED stopped=true delayedResume=false finishing=false
keysPaused=false inHistory=true visible=false sleeping=false idle=true
fullscreen=true noDisplay=false immersive=false launchMode=0
frozenBeforeDestroy=false forceNewConfig=false
mActivityType=APPLICATION_ACTIVITY_TYPE
waitingVisible=false nowVisible=false lastVisibleTime=-15s961ms
* Hist #0: ActivityRecord{50bedc3 u0 com.didi.taskstack/.MainActivity t338}
packageName=com.didi.taskstack processName=com.didi.taskstack
launchedFromUid=2000 launchedFromPackage=null userId=0
app=ProcessRecord{362d717 29425:com.didi.taskstack/u0a113}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.didi.taskstack/.MainActivity }
frontOfTask=true task=TaskRecord{57d640 #338 A=com.didi.taskstack U=0 sz=4}
taskAffinity=com.didi.taskstack
realActivity=com.didi.taskstack/.MainActivity
baseDir=/data/app/com.didi.taskstack-1/base.apk
dataDir=/data/user/0/com.didi.taskstack
stateNotNeeded=false componentSpecified=true mActivityType=0
compat={480dpi} labelRes=0x7f060015 icon=0x7f030000 theme=0x7f090037
config={1.0 ?mcc?mnc zh_CN ldltr sw360dp w360dp h580dp 480dpi nrml port finger -keyb/v/h -nav/h suim:1 s.10}
stackConfigOverride={1.0 ?mcc?mnc ?locale ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?}
taskDescription: iconFilename=null label=”null” color=ff3f51b5
launchFailed=false launchCount=0 lastLaunchTime=-26s659ms
haveState=true icicle=Bundle[mParcelledData.dataSize=620]
state=STOPPED stopped=true delayedResume=false finishing=false
keysPaused=false inHistory=true visible=false sleeping=false idle=true
fullscreen=true noDisplay=false immersive=false launchMode=0
frozenBeforeDestroy=false forceNewConfig=false
mActivityType=APPLICATION_ACTIVITY_TYPE
waitingVisible=false nowVisible=false lastVisibleTime=-26s336ms
Task id #340
* TaskRecord{5a660a9 #340 A=com.didi.taskstack U=0 sz=1}
userId=0 effectiveUid=u0a113 mCallingUid=u0a113 mCallingPackage=com.didi.taskstack
affinity=com.didi.taskstack
intent={flg=0x10000000 cmp=com.didi.taskstack/.SingleInstanceActivity}
realActivity=com.didi.taskstack/.SingleInstanceActivity
autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=0 mTaskToReturnTo=0
rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
Activities=[ActivityRecord{9f7ec30 u0 com.didi.taskstack/.SingleInstanceActivity t340}]
askedCompatMode=false inRecents=false isAvailable=true
lastThumbnail=null lastThumbnailFile=/data/system/recent_images/340_task_thumbnail.png
stackId=30
hasBeenVisible=true mResizeable=false firstActiveTime=1472561975670 lastActiveTime=1472561975670 (inactive for 8s)
otherMwTaskId=0
* Hist #0: ActivityRecord{9f7ec30 u0 com.didi.taskstack/.SingleInstanceActivity t340}
packageName=com.didi.taskstack processName=com.didi.taskstack
launchedFromUid=10113 launchedFromPackage=com.didi.taskstack userId=0
app=ProcessRecord{362d717 29425:com.didi.taskstack/u0a113}
Intent { flg=0x10000000 cmp=com.didi.taskstack/.SingleInstanceActivity }
frontOfTask=true task=TaskRecord{5a660a9 #340 A=com.didi.taskstack U=0 sz=1}
taskAffinity=com.didi.taskstack
realActivity=com.didi.taskstack/.SingleInstanceActivity
baseDir=/data/app/com.didi.taskstack-1/base.apk
dataDir=/data/user/0/com.didi.taskstack
stateNotNeeded=false componentSpecified=true mActivityType=0
compat={480dpi} labelRes=0x7f060015 icon=0x7f030000 theme=0x7f09008d
config={1.0 ?mcc?mnc zh_CN ldltr sw360dp w360dp h580dp 480dpi nrml port finger -keyb/v/h -nav/h suim:1 s.10}
stackConfigOverride={1.0 ?mcc?mnc ?locale ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?}
taskDescription: iconFilename=null label=”null” color=ff3f51b5
launchFailed=false launchCount=0 lastLaunchTime=-10s384ms
haveState=true icicle=Bundle[mParcelledData.dataSize=620]
state=STOPPED stopped=true delayedResume=false finishing=false
keysPaused=false inHistory=true visible=false sleeping=false idle=true
fullscreen=true noDisplay=false immersive=false launchMode=3
frozenBeforeDestroy=false forceNewConfig=false
mActivityType=APPLICATION_ACTIVITY_TYPE
waitingVisible=false nowVisible=false lastVisibleTime=-10s57ms
Task id #339
* TaskRecord{4c69583 #339 A=com.didi.qiaoyf U=0 sz=1}
userId=0 effectiveUid=u0a113 mCallingUid=u0a113 mCallingPackage=com.didi.taskstack
affinity=com.didi.qiaoyf
intent={flg=0x10000000 cmp=com.didi.taskstack/.TaskAffinityActivity}
realActivity=com.didi.taskstack/.TaskAffinityActivity
autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=0 mTaskToReturnTo=0
rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
Activities=[ActivityRecord{fbae432 u0 com.didi.taskstack/.TaskAffinityActivity t339}]
askedCompatMode=false inRecents=true isAvailable=true
lastThumbnail=android.graphics.Bitmap@17d1953 lastThumbnailFile=/data/system/recent_images/339_task_thumbnail.png
stackId=30
hasBeenVisible=true mResizeable=false firstActiveTime=1472561973651 lastActiveTime=1472561973651 (inactive for 10s)
otherMwTaskId=0
* Hist #0: ActivityRecord{fbae432 u0 com.didi.taskstack/.TaskAffinityActivity t339}
packageName=com.didi.taskstack processName=com.didi.taskstack
launchedFromUid=10113 launchedFromPackage=com.didi.taskstack userId=0
app=ProcessRecord{362d717 29425:com.didi.taskstack/u0a113}
Intent { flg=0x10000000 cmp=com.didi.taskstack/.TaskAffinityActivity }
frontOfTask=true task=TaskRecord{4c69583 #339 A=com.didi.qiaoyf U=0 sz=1}
taskAffinity=com.didi.qiaoyf
realActivity=com.didi.taskstack/.TaskAffinityActivity
baseDir=/data/app/com.didi.taskstack-1/base.apk
dataDir=/data/user/0/com.didi.taskstack
stateNotNeeded=false componentSpecified=true mActivityType=0
compat={480dpi} labelRes=0x7f060015 icon=0x7f030000 theme=0x7f09008d
config={1.0 ?mcc?mnc zh_CN ldltr sw360dp w360dp h580dp 480dpi nrml port finger -keyb/v/h -nav/h suim:1 s.10}
stackConfigOverride={1.0 ?mcc?mnc ?locale ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/?}
taskDescription: iconFilename=null label=”null” color=ff3f51b5
launchFailed=false launchCount=0 lastLaunchTime=-13s481ms
haveState=true icicle=Bundle[mParcelledData.dataSize=620]
state=STOPPED stopped=true delayedResume=false finishing=false
keysPaused=false inHistory=true visible=false sleeping=false idle=true
fullscreen=true noDisplay=false immersive=false launchMode=0
frozenBeforeDestroy=false forceNewConfig=false
mActivityType=APPLICATION_ACTIVITY_TYPE
waitingVisible=false nowVisible=false lastVisibleTime=-13s149ms

從上面可以看到任務棧和Activity啓動配置的很多信息,這裏我們重點看一下任務棧的Activity實例和壓入順序,taskAffinity,launchMode(0:Standard,1:singleTop,2:singleTask,3:singleInstance),processName.
應用程序爲com.didi.taskstack的app運行過程中,創建了三個任務棧Task id #338,Task id #340,Task id #339。
Task id #338:Activities=[ActivityRecord{50bedc3 u0 com.didi.taskstack/.MainActivity t338}, ActivityRecord{489b3da u0 com.didi.taskstack/.StandardActivity t338}, ActivityRecord{8b6c160 u0 com.didi.taskstack/.MainActivity t338}, ActivityRecord{3d149db u0 com.android.contacts/.activities.ContactSelectionActivity t338}]
其中ContactSelectionActivity爲系統選擇聯繫人頁面,launchMode爲singleTop,taskAffinity爲android.task.contacts,進程processName=com.android.contacts,其他三個均爲應用自身的Activity,taskAffinity爲應用自身默認的包名。
Task id #340:Activities=[ActivityRecord{9f7ec30 u0 com.didi.taskstack/.SingleInstanceActivity t340}]
SingleInstanceActivity launch mode爲singleIntance,單獨爲其創建一個task,但是taskAffinity爲應用自身默認的包名。
Task id #339:Activities=[ActivityRecord{fbae432 u0 com.didi.taskstack/.TaskAffinityActivity t339}]
TaskAffinityActivity爲應用自身的Activity,修改TaskAffinityActivity的taskAffinity爲com.didi.qiaoyf,創建了一個taskAffinity爲com.didi.qiaoyf的任務棧。
由以上分析可知,一個應用至少有一個默認taskAffinity的任務棧,可以通過singleInstance或者修改Activity的taskAffinity來創建不同的任務棧,同屬一個任務棧的Actvity也可能運行在不同的進程。從存在形態來講,任務是指在執行特定作業時與用戶交互的一系列 Activity, 這些 Activity 按照各自的打開順序排列在堆棧(即“返回棧”)中。

二、Activity任務棧的算法分析

Activity任務棧,從數據結構上來講,可以理解爲滿足特定push算法的,存放Activity信息的棧。主要算法描述如下:
假定由Context A startActivity到 Activity B。

當Context A 是Activity:
每個Activity都有個taskAffinity,默認爲應用程序的包名,也可以通過android:taskAffinity設定,故分爲以下兩種情況:
1. Activity A 和 Activity B taskAffinity相同
情形1:A的launchMode 不爲singleInstance時
(1)standard,標準模式,每次startActivity都會創建一個新的實例,壓入A所在任務棧中;
(2)singleTop,棧頂複用模式,每次startActivity都會先檢查A所在任務棧棧頂是否存在一個該Activity一個的實例,如果有則不會創建新實例,如果沒有才會創建一個新的實例壓入棧中;
(3)singleTask,每次startactivity都會先檢查A所在任務棧棧中是否存在一個該Activity一個的實例,如果有則彈出棧中在該activity實例之上的所有activity實例,複用棧中的該Activity實例,如果沒有才會創建一個新的實例壓入棧中;
(4)singleInstance,與 “singleTask” 相同,如果存在則複用該任務棧,如果沒有,系統會爲他單獨創建一個任務棧,且任何其他 Activity 實例都不會壓入該棧中,該 Activity 始終是其任務唯一僅有的成員,該棧的affinity與A的任務棧一致。
情形2:A的launchMode 爲singleInstance時
壓入算法與上述保持一致,但由於A的任務棧有且只能由Activity A,因此Activity B只能尋找affinity與A一致且不是獨佔的任務棧。
2. Activity A 和 Activity B taskAffinity不相同
I. A的launchMode 不爲singleInstance時
情形3:啓動 Activity B的 Intent 沒有包含 FLAG_ACTIVITY_NEW_TASK 標誌並且Activity B的launchMode 不是singleTask,singleInstance。
(1)standard,標準模式,每次startActivity都會創建一個新的實例,壓入A所在任務棧中。
(2)singleTop,棧頂複用模式,每次startActivity都會先檢查A所在任務棧棧頂是否存在一個該Activity一個的實例,如果有則不會創建新實例,如果沒有才會創建一個新的實例壓入棧中
情形4:啓動 Activity B的 Intent 包含 FLAG_ACTIVITY_NEW_TASK 標誌或者Activity B的launchMode 是singleTask或者singleInstance。
(1)standard,標準模式,每次startActivity都會創建一個新的實例,
然後尋找affinity與B一致且不是獨佔的任務棧,如果沒有則創建新任務棧並壓入該棧中。
(2)singleTop,棧頂複用模式,每次startActivity都會先尋找affinity與B一致且不是獨佔的任務棧,如果沒有則創建新任務棧,然後檢查該任務棧棧頂是否存在一個該Activity的實例,如果有則不會創建新實例,如果沒有才會創建一個新的實例壓入棧中
(3)singleTask,每次startActivity都會先尋找affinity與B一致且不是獨佔的任務棧,如果沒有則創建新任務棧,然後檢查該任務棧棧中是否存在一個該Activity一個的實例,如果有則彈出棧中在該Activity實例之上的所有Activity實例,複用棧中的該Activity實例,如果沒有才會創建一個新的實例壓入棧中。
(4)singleInstance,與 “singleTask” 相同,如果存在則複用該任務棧,如果沒有,則系統會爲他單獨創建一個affinity與B一致的任務棧,且任何其他 Activity 實例都不會壓入該棧中。
II. A的launchMode 爲singleInstance時
壓入棧策略與情形4一致。

當Context A 不是Activity:
只能通過包含 FLAG_ACTIVITY_NEW_TASK 標誌的Intent來啓動Activity B,壓入棧策略與情形4一致。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章