App打包apk安裝後重複啟動根界面的問題
這個問題很特殊,一般情況下很難被發現,是Android系統一直以來的一個Bug。
當我們把app打包成apk安裝程式,通過點選apk檔案進行安裝時,會啟動安裝界面,
并在安裝成功後會跳轉安裝完成界面,
如圖:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiEkbZxGZzwEMGdlY5lTbaZXUE5ke4kHZ2lkeMl3YYpFcaZlWuZ0ViBnTw4EbBhVY5J1MjZXUuJGbs12Y2FTeiBjVYlldJp2YulzVUxGZXlFdsJDU3VlaOdXVtlFbkpXT1UUbNFTSEpFNwknTwkFVOBTUq1Ee4k3YsR2VZRHbyg1aGJjYzJEWkZHOXFWdVhUY6VzVZBHctxkeWJjWoFzVhRXUXlld4d0YxkTeMZTTINGMShUYvwlbj5yZtlmbkN3YuQnclZnbvN2Ztl2Lc9CX6MHc0RHaiojIsJye.jpg)
安裝完成界面
我們點選圖中的打開按鈕,此時會啟動我們的app
這裡為了讓大家更容易了解一些,我們假設app有兩個界面
啟動界面SplashActivity
主界面MainActivity
app啟動後打開SplashActivity,3秒後自動跳轉MainActivity,界面不做強制finish
接下來,我們需要了解下Task任務棧和Back Stack傳回棧,
如果有同學對這兩個概念還不熟悉的,
可以看一下官方文檔,講得很詳細:
Android任務和傳回棧官方文檔
這裡我們引用官方文檔的一句話:
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.
當我們點選home界面的應用啟動圖示時(安裝完成,界面點選icon打開同理)
如果沒有對應Task任務棧存在,則會建立一個新的任務棧,
并且把應用啟動的首頁面作為根Activity放到任務棧中。
如果存在對應的Task任務棧,則會直接調用對應的Task任務棧到前台,并将棧頂的界面顯示給使用者,
那麼當我們的app啟動後打開SplashActivity并跳轉主界面MainActivity後,我們app的任務棧應該如圖所示:
此時,當我們點選Home鍵退回到桌面,
app的Task任務棧進入背景,然後我們點選桌面上的啟動圖示,正常情況下,app應該會把它對應的Task任務棧調到前台,并顯示剛剛棧頂的MainActivity界面,
正常流程:
然而,實際情況是,app會把它的Task任務棧調用到前台,
并在任務棧上重新建立新的SplashActivity ,再跳轉到MainActivity,
在不重新加載application的情況下,它又重新走了一遍啟動的流程,這個時候,我們會發現任務棧中的Activity重複了,SplashActivity跟MainActivity都變成了兩個
為了更清晰的讓大家了解,這裡畫了兩個圖,
錯誤的bug流程
錯誤狀态下的Task任務棧
bug流程:
新調用的SplashActivity會被置于該app的task棧頂
多出了兩個Activity
當然這個bug一般使用者也很難注意到,它的産生必須滿足下面的條件:
點選apk檔案安裝app
安裝完成界面點選打開按鈕
點選Home鍵,進入系統桌面,此時app退到背景
再點選桌面上啟動圖示
那麼對于這種問題我們如何來處理呢?
按照上文的舉例,
在正常流程下啟動app進入MainActivity界面時的任務棧:
正常情況
bug情況下,會調起任務棧到前台并添加根Acitivy SplashActivity到棧頂,此時的任務棧:
我們可以看到,在bug情況下啟動app時,SplashActivity(app的根Activity)再次建立并疊加到Task任務棧上了
理應隻會出現在棧底的SplashActivity出現在了其他位置,是以這裡我們直接判斷了app根Activity SplashActivity的位置
在app的SplashActivity(app的根Activity)的onCreate方法中通過 isTaskRoot() 方法來判斷是否是任務棧中的根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;
}
}
}
這樣棧頂的SplashActivity在還未執行其他代碼的情況下就finish()掉了,此時會顯示棧頂的MainActivity。