個(gè)人博客地址:https://blog.yookingh.cn
該文章地址:https://blog.yookingh.cn/dev/190806-home-Restart.html
BUG描述
操作流程:打release包——安裝apk——點(diǎn)選“打開”按鈕——點(diǎn)Home鍵——點(diǎn)桌面圖標(biāo)
現(xiàn)象:App重啟
-
原理分析及分析過程:
-
比較【安裝apk直接打開】時(shí)的intent和【桌面圖標(biāo)打開】的intent的區(qū)別
直接打開:(Logcat直接搜索
I/ActivityManager: START u0
)I/ActivityManager: START u0 { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.yooking.test cmp=com.yooking.test/.SplashActivity } from uid 10039
桌面圖標(biāo)打開:
I/ActivityManager: START u0 { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.yooking.test/.SplashActivity bnds=[856,662][1067,951] } from uid 10068
對(duì)比結(jié)果:顯然【桌面圖標(biāo)打開】時(shí)比【安裝apk直接打開】少了一條pkg,多了一條bnds(pkg即Package,bnds即SourceBounds)
-
猜測:懷疑bug的原因是pkg導(dǎo)致椎椰,那就模擬下唄:(紅波浪線是因?yàn)锧IntDef)
setClickListener(findViewById(R.id.button),(view)->{//模擬直接打開 Intent intent = new Intent(Intent.ACTION_MAIN);//act intent.addCategory(Intent.CATEGORY_LAUNCHER);//cat intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));//flg 0x10000000 intent.setPackage("com.yooking.test");//pkg ComponentName cmp = ComponentName.unflattenFromString("com.yooking.test/.SplashActivity"); intent.setComponent(new ComponentName());//cmp startActivity(intent); }) setClickListener(findViewById(R.id.button),(view)->{//模擬桌面打開 Intent intent = new Intent(Intent.ACTION_MAIN);//act intent.addCategory(Intent.CATEGORY_LAUNCHER);//cat intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED));//flg 0x10200000 ComponentName cmp = ComponentName.unflattenFromString("com.yooking.test/.SplashActivity"); intent.setComponent(new ComponentName());//cmp startActivity(intent); })
結(jié)論:模擬結(jié)果與猜測一致礼烈,帶有pkg參數(shù)的意圖跳轉(zhuǎn)時(shí),點(diǎn)home鍵確實(shí)會(huì)出現(xiàn)app重啟現(xiàn)象
-
解決BUG
-
bug的成因分析出來了狼电,如何解決?
根據(jù)前述分析,我們認(rèn)為BUG出現(xiàn)的原因是Android手機(jī)下載app后直接打開時(shí)所傳參數(shù)比桌面圖標(biāo)打開多了一個(gè)pkg導(dǎo)致的疾就。
-
解決方案1:莽過去!
什么叫莽過去艺蝴?就是直截了當(dāng)猬腰,干脆利落,你不是多傳了個(gè)pkg嗎吴趴,那我把你關(guān)了漆诽,再自己啟動(dòng)一個(gè)不就行了?boolean firstStart @Override protected void onCreate(Bundle savedInstanceState) { if(firstStart){ Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setComponent(new ComponentName(BuildConfig.APPLICATION_ID,SplashActivity.class)); intent.addFlags(0x10200000); startActivity(intent); finish(); } }
just look 挺 perfect 的...事實(shí)上嘛...就當(dāng)無事發(fā)生好了...
原因:帶pkg和不帶pkg被認(rèn)為非同一應(yīng)用锣枝,故而繼續(xù)啟動(dòng)了Splash頁面 -
解決方案2:分析BUG現(xiàn)象厢拭,根據(jù)現(xiàn)象解決問題
目標(biāo)是什么:App啟動(dòng)不重啟
不重啟意味著什么:點(diǎn)擊Home鍵時(shí)的Activity回歸任務(wù)棧頂部任務(wù)棧:GET_TASKS權(quán)限于Android L廢棄,REAL_GET_TASKS權(quán)限僅限系統(tǒng)應(yīng)用有效F踩9! 因此陨闹,無法在App日志中查看任務(wù)棧信息楞捂。 任務(wù)棧查看方法:Android Studio 中 Terminal 輸入 adb shell dumpsys activity activities
由于我在SplashActivity中寫了finish方法,可以分別看到:
#0直接啟動(dòng):Running activities (most recent first): TaskRecord{ea096c0 #1597 A=[packageName] U=0 StackId=1604 sz=1} Run #0: ActivityRecord{e30acd0 u0 [packageName]/[packageName].activity.MainActivity t1597} mResumedActivity: ActivityRecord{e30acd0 u0 [packageName]/[packageName].activity.MainActivity t1597} mLastPausedActivity: ActivityRecord{f68c709 u0 [packageName]/[packageName].activity.SplashActivity t-1f}
#1點(diǎn)擊Home鍵后趋厉,再次打開寨闹,在Splash頁面時(shí)的任務(wù)棧:
Running activities (most recent first): TaskRecord{ea096c0 #1597 A=[packageName] U=0 StackId=1604 sz=2} Run #1: ActivityRecord{433989 u0 [packageName]/[packageName].activity.SplashActivity t1597} Run #0: ActivityRecord{e30acd0 u0 [packageName]/[packageName].activity.MainActivity t1597}
兩者對(duì)比,可以看到任務(wù)棧中Splash兩次的id是不同的君账,而main是一致的繁堡。App并非重啟,只是多啟動(dòng)了一次Splash乡数。
可見:我們只需要消除往后打開時(shí)的Splash頁面即可椭蹄。
如何做?根據(jù)上述分析净赴,有如下判斷在SplashActivity中 if(SplashActivity在 任務(wù)棧最底部 ){ //App為正常啟動(dòng) }else{ //App為已經(jīng)啟動(dòng)并且點(diǎn)擊Home鍵后再次打開 關(guān)閉被錯(cuò)誤打開的SplashActivity }
判斷是否為 任務(wù)棧最底部的 源碼:
/** * Return whether this activity is the root of a task. The root is the * first activity in a task. * * @return True if this is the root activity, else false. */ @Override public boolean isTaskRoot() { try { return ActivityManager.getService().getTaskForActivity(mToken, true) >= 0; } catch (RemoteException e) { return false; } }
判斷方法:
public class SplashActivity extends BaseActivity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (!this.isTaskRoot()) {//判斷是否為根Activity Intent mainIntent = getIntent(); String action = mainIntent.getAction(); //判斷是否為啟動(dòng)頁 如果App不會(huì)因?yàn)槠渌蛘{(diào)用該頁面绳矩,可以不寫if語句 if (mainIntent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(action)) { finish(); return; } } } }
這個(gè)代碼應(yīng)該緊隨著
super.onCreate(savedInstanceState);
之后。
onCreate確保當(dāng)前Activity是正在創(chuàng)建狀態(tài)玖翅,而在setContentView之前翼馆,確保Activity是未渲染的割以,此時(shí)關(guān)閉不會(huì)讓用戶察覺
總結(jié)
方案二雖然確實(shí)能夠解決Home鍵重啟問題,但是也會(huì)導(dǎo)致App啟動(dòng)速度變慢(有一個(gè)白屏過程)写妥,而這個(gè)問題在App使用360/樂固等加固后拳球,更為嚴(yán)重,這或許是需要進(jìn)一步解決的珍特。
-
我在項(xiàng)目中用了取巧的解決方案:迷惑用戶——將白屏替換成啟動(dòng)頁祝峻。為啟動(dòng)頁設(shè)置一個(gè)Theme(僅限啟動(dòng)頁)
<style name="Splash_Theme" parent="AppTheme_NoActionBar"> <item name="android:windowBackground">@drawable/splash</item> </style>