無(wú)意中發(fā)現(xiàn)的一個(gè)bug,測(cè)試了一下很多設(shè)備上很多APP都有這種情況,包括部分知名公司產(chǎn)品,研究了一下發(fā)現(xiàn)只是很早就存在的一個(gè)系統(tǒng)bug,以前竟然沒(méi)能留意到。
如何重現(xiàn)
瀏覽器下載、QQ下載或者通過(guò)軟件市場(chǎng)下載安裝后,直接在安裝成功頁(yè)面點(diǎn)擊打開(kāi),程序正常啟動(dòng)閃屏頁(yè)面,然后進(jìn)入主頁(yè)面。此時(shí)按“Home”鍵讓程序切后臺(tái),再在桌面上點(diǎn)擊此應(yīng)用的圖標(biāo),理論上應(yīng)該直接恢復(fù)到剛才的主頁(yè)面,結(jié)果此時(shí)程序重新顯示閃屏頁(yè),然后進(jìn)入主頁(yè)。通過(guò)一路按返回,可以發(fā)現(xiàn)每次按“Home”恢復(fù)時(shí)都是重新創(chuàng)建了新的Activity。
這個(gè)問(wèn)題說(shuō)起來(lái)也是比較嚴(yán)重的,用戶安裝了一個(gè)應(yīng)用,啟動(dòng)然后手機(jī)注冊(cè),程序切后臺(tái)查看短信注冊(cè)碼,再切回去發(fā)現(xiàn)程序從頭走了。如果一路back讓程序退出,再次從桌面icon啟動(dòng)應(yīng)用則不會(huì)出現(xiàn)問(wèn)題
分析
這個(gè)問(wèn)題第一反應(yīng)就是看下Activity的flag,在閃屏頁(yè)打一個(gè)Log,發(fā)現(xiàn)安裝后直接打開(kāi)的Flag值為0x10000000,即FLAG_ACTIVITY_NEW_TASK,此時(shí)按Home切后臺(tái)再按桌面Icon,閃屏頁(yè)面的Flag值為0x10600000,即這個(gè)Flag導(dǎo)致的這個(gè)奇怪的問(wèn)題。而程序退出正常從桌面Icon啟動(dòng)的Flag應(yīng)該為0x10200000(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)。上面出問(wèn)題的那個(gè)Flag中的值600000再類Intent中并沒(méi)有找到,但是通過(guò)位運(yùn)算不難發(fā)現(xiàn)它是200000和400000的或值,即0x10600000的值為(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_BROUGHT_TO_FRONT)。通過(guò)搜索發(fā)現(xiàn)這是一個(gè)系統(tǒng)bug,Issue Tracker上面很早都有人提出這個(gè)問(wèn)題了,但一直沒(méi)有解決。我找了一下,也沒(méi)能找到引起這個(gè)問(wèn)題的原因,大家有誰(shuí)研究過(guò)這一塊的源碼找到原因的希望能給我講一下。
解決方案
問(wèn)題的解決方案已經(jīng)明確了,就是在我們的root activity Flag值為0x10600000的這種情況不處理即可。stackoverflow中和Issue Tracker中都有人提出了一個(gè)認(rèn)可程度比較高的解決方案,即在應(yīng)用的root acitivity的onCreate()中加入以下代碼:
if (!isTaskRoot()) {
Intent intent = getIntent();
String action = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
finish();
return;
}
}
這個(gè)是從判斷是否root task來(lái)處理的,通過(guò)打log可以確定問(wèn)題出現(xiàn)時(shí)雖然每次運(yùn)行都是都是新建一個(gè)閃屏頁(yè)面,但是它的taskId并沒(méi)有改變,所以這個(gè)方案不失為一個(gè)不錯(cuò)的解決方案。
在最開(kāi)始分析這個(gè)問(wèn)題原因的時(shí)候,反編譯了一些不能重新此問(wèn)題的APP,例如美團(tuán)外賣,我在它的閃屏頁(yè)面里面的onCreate()函數(shù)里面發(fā)現(xiàn)如下代碼:
if ((getIntent().getFlags() & 0x400000) != 0) {
finish();
return;
}
這個(gè)方法與我們上面的分析不謀而合,通過(guò)簡(jiǎn)單測(cè)試這兩種方法的都能解決問(wèn)題。