App打包apk安裝后重復(fù)啟動(dòng)根界面的問(wèn)題
這個(gè)問(wèn)題很特殊蝇摸,一般情況下很難被發(fā)現(xiàn)壤圃,是Android系統(tǒng)一直以來(lái)的一個(gè)Bug夹孔。
當(dāng)我們把a(bǔ)pp打包成apk安裝程序被盈,通過(guò)點(diǎn)擊apk文件進(jìn)行安裝時(shí)析孽,會(huì)啟動(dòng)安裝界面,
并在安裝成功后會(huì)跳轉(zhuǎn)安裝完成界面只怎,
如圖:
我們點(diǎn)擊圖中的打開(kāi)按鈕袜瞬,此時(shí)會(huì)啟動(dòng)我們的app
這里為了讓大家更容易理解一些,我們假設(shè)app有兩個(gè)界面
- 啟動(dòng)界面SplashActivity
- 主界面MainActivity
- app啟動(dòng)后打開(kāi)SplashActivity身堡,3秒后自動(dòng)跳轉(zhuǎn)MainActivity,界面不做強(qiáng)制finish
接下來(lái)邓尤,我們需要了解下Task任務(wù)棧和Back Stack返回棧,
如果有同學(xué)對(duì)這兩個(gè)概念還不熟悉的贴谎,
可以看一下官方文檔汞扎,講得很詳細(xì):
Android任務(wù)和返回棧官方文檔
這里我們引用官方文檔的一句話:
The device Home screen is the starting place for most tasks. When the user touches an icon in the application launcher (or a shortcut on the Home screen), that application's task comes to the foreground. If no task exists for the application (the application has not been used recently), then a new task is created and the "main" activity for that application opens as the root activity in the stack.
當(dāng)我們點(diǎn)擊home界面的應(yīng)用啟動(dòng)圖標(biāo)時(shí)(安裝完成,界面點(diǎn)擊icon打開(kāi)同理)
如果沒(méi)有對(duì)應(yīng)Task任務(wù)棧存在,則會(huì)創(chuàng)建一個(gè)新的任務(wù)棧擅这,
并且把應(yīng)用啟動(dòng)的首頁(yè)面作為根Activity放到任務(wù)棧中澈魄。
如果存在對(duì)應(yīng)的Task任務(wù)棧,則會(huì)直接調(diào)用對(duì)應(yīng)的Task任務(wù)棧到前臺(tái)蕾哟,并將棧頂?shù)慕缑骘@示給用戶,
那么當(dāng)我們的app啟動(dòng)后打開(kāi)SplashActivity并跳轉(zhuǎn)主界面MainActivity后一忱,我們app的任務(wù)棧應(yīng)該如圖所示:
此時(shí)莲蜘,當(dāng)我們點(diǎn)擊Home鍵退回到桌面谭确,
app的Task任務(wù)棧進(jìn)入后臺(tái),然后我們點(diǎn)擊桌面上的啟動(dòng)圖標(biāo)票渠,正常情況下逐哈,app應(yīng)該會(huì)把它對(duì)應(yīng)的Task任務(wù)棧調(diào)到前臺(tái),并顯示剛剛棧頂?shù)腗ainActivity界面问顷,
正常流程:
然而昂秃,實(shí)際情況是,app會(huì)把它的Task任務(wù)棧調(diào)用到前臺(tái)杜窄,
并在任務(wù)棧上重新創(chuàng)建新的SplashActivity 肠骆,再跳轉(zhuǎn)到MainActivity,
在不重新加載application的情況下塞耕,它又重新走了一遍啟動(dòng)的流程蚀腿,這個(gè)時(shí)候,我們會(huì)發(fā)現(xiàn)任務(wù)棧中的Activity重復(fù)了扫外,SplashActivity跟MainActivity都變成了兩個(gè)
為了更清晰的讓大家理解莉钙,這里畫(huà)了兩個(gè)圖,
- 錯(cuò)誤的bug流程
- 錯(cuò)誤狀態(tài)下的Task任務(wù)棧
bug流程:
新調(diào)用的SplashActivity會(huì)被置于該app的task棧頂
多出了兩個(gè)Activity
當(dāng)然這個(gè)bug一般用戶也很難注意到筛谚,它的產(chǎn)生必須滿足下面的條件:
- 點(diǎn)擊apk文件安裝app
- 安裝完成界面點(diǎn)擊打開(kāi)按鈕
- 點(diǎn)擊Home鍵磁玉,進(jìn)入系統(tǒng)桌面,此時(shí)app退到后臺(tái)
- 再點(diǎn)擊桌面上啟動(dòng)圖標(biāo)
那么對(duì)于這種問(wèn)題我們?nèi)绾蝸?lái)處理呢?
按照上文的舉例驾讲,
在正常流程下啟動(dòng)app進(jìn)入MainActivity界面時(shí)的任務(wù)棧:
bug情況下蚊伞,會(huì)調(diào)起任務(wù)棧到前臺(tái)并添加根Acitivy SplashActivity到棧頂席赂,此時(shí)的任務(wù)棧:
我們可以看到,在bug情況下啟動(dòng)app時(shí)时迫,SplashActivity(app的根Activity)再次創(chuàng)建并疊加到Task任務(wù)棧上了
理應(yīng)只會(huì)出現(xiàn)在棧底的SplashActivity出現(xiàn)在了其他位置氧枣,所以這里我們直接判斷了app根Activity SplashActivity的位置
在app的SplashActivity(app的根Activity)的onCreate方法中通過(guò) isTaskRoot() 方法來(lái)判斷是否是任務(wù)棧中的根Activity,如果是就不做任何處理别垮,如果不是則直接finish掉;
public class SplashActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setTheme(R.style.AppTheme_NoActionBar);
super.onCreate(savedInstanceState);
if (!isTaskRoot()) {
finish();
return;
}
}
}
這樣棧頂?shù)腟plashActivity在還未執(zhí)行其他代碼的情況下就finish()掉了便监,此時(shí)會(huì)顯示棧頂?shù)腗ainActivity。