問題
問題描述:我們的app被其他app啟動進入MainActivity,然後MainActivity連續呼叫兩次startActivity分別啟動IndexActivity和CommonActivity,先啟動的IndexActivity,然後啟動的CommonActivity,然後神奇的是螢幕上顯示的是IndexActivity,按返回關閉IndexActivity後才顯示CommonActivity。
這個問題是同事在做被第三方app開啟這個功能時發現的,他自己寫的demo測試都是正常的,移到我們專案上來就不行了,他對比了下發現是IndexActivity的launchMode設定的singleTask,去掉就是正常的了,但是這個singleTask又是其他功能需要的不能去(這個功能是我加的),所以最後反饋到我這了。
當時覺得是很神奇但是又不知道為什麼,自己跑了下確實像同事說的去掉singleTask就好了,考慮到啟動模式會影響activity的任務棧,想先看一下加了singleTask時的任務棧和去掉後的任務棧有什麼區別。然後就百度了下,確實可以檢視當前的任務棧,adb命令
adb shell dumpsys activity activities
具體列印出來的資訊很多,這裡就不放了,說一下從裡面發現了什麼。
結果
假定第三方app為App1,我們的app為App2,App1的TestActivity啟動App2的MainActivity,然後MainActivity連續呼叫兩次startActivity分別啟動IndexActivity和CommonActivity,先啟動的IndexActivity,然後啟動的CommonActivity,其中IndexActivity的LaunchMode設定singleTask,。 預期的activity棧如下:
但實際上現在是存在兩個activity棧了: 此時MainActivity和CommonActivity和App1的TestActivity在同一個任務棧,IndexActivity因為singleTask是會新開一個任務棧,(如果IndexActivity沒有指定taskAffinity,那麼這個任務棧名字就是App2包名,如果指定了taskAffinity就會用taskAffinity指定的名字),本來CommonActivity是在IndexActivity之後啟動的,但是因為IndexActivity所在任務棧是新建立的,這樣就會使IndexActivity在前臺而顯示出來,顯示在最上面,此時按返回鍵會先finish IndexActivity然後顯示出來CommonActivity。以普通模式啟動的activity會進入啟動它的activity的任務棧,所以當MainActivity啟動了IndexActivity和CommonActivity,因為MainActivity是普通模式啟動的,會被加到App1的TestActivity所在的任務棧,CommonActivity也是普通模式啟動,會被加到MainActivity所在任務棧也就是TestActivity的任務棧,設定他們的taskAffinity也沒用(只有singleTask,singleInstance這樣會建立新任務棧的才有用),這樣MainActivity和CommonActivity都在TestActivity所在的任務棧中。
解決
要想達到前臺顯示的是CommonActivity,就必須讓MainActivity、IndexActivity、CommonActivity在一個任務棧,所以可以把MainActivity的啟動模式也設定為singleTask,這樣MainActivity被加到了一個新任務棧(如果不指定taskAffinity,那麼這個任務棧名字就是App2包名),MainActivity再啟動了IndexActivity和CommonActivity,並且IndexActivity不指定taskAffinity(預設使用包名作為任務棧名),這樣和MainActivity任務棧名字一樣,加到MainActivity所在任務棧,CommonActivity是普通模式啟動,也會加入到啟動者MainActivity所在任務棧,這樣三者就都在一個任務棧中了,因為CommonActivity是後啟動的,它會顯示在前臺,所以問題就是這樣。如下:
附加
還有singleInstance啟動的,同樣指定了taskAffinity就用指定的作為任務棧名,沒有指定就用包名,會建立一個新任務棧A(如果有同名的任務棧表示已經建立過了就重用,不會建立新任務棧)並且裡面只有一個Activity,哪怕後面他又啟動了一個singleTask的Activity並且任務棧名字相同也不會影響任務棧A,singleInstance啟動的Activity所在的任務棧如果沒有就建立,如果有就重用,並且裡面只有一個Activity。
思考
前面提到的taskAffinity,回到最開始的問題,假如我把MainActivity的taskAffinity指定為App2包名,這樣雖然App2的IndexActivity的啟動模式是singleTask,但是他們任務棧名字一樣應該會在同一個任務棧吧。其實是不行的,taskAffinity可以和allowTaskReparenting配合使用,當allowTaskReparenting設為true時,同時指定taskAffinity等於App2的包名,還是像開始的那樣操作,MainActivity和CommonActivity還是會在App1的TestActivity的任務棧中,當按了Home鍵回到桌面後在開啟App2,此時MainActivity才會在以App2包名命名的任務棧中。