singleTop啟動(dòng)模式真的可以防止多次打開棧頂?shù)腁ctivity么?
開發(fā)過程中我們經(jīng)常會(huì)遇到各式各樣的bug,比如說測(cè)試小姐姐告訴我們,由于無操作,某個(gè)按鈕她快速點(diǎn)擊了兩次
(或者由于卡頓之類的延遲),打開了兩個(gè)詳情頁(yè)
,希望把這個(gè)禁止掉
,只讓打開一個(gè)詳情頁(yè)。
快速點(diǎn)擊按鈕,多次打開默認(rèn)啟動(dòng)模式的Activity
我們先來復(fù)現(xiàn)下這種場(chǎng)景,Activity配置和按鈕點(diǎn)擊代碼很簡(jiǎn)單,如下所示:
activity配置:
<activity
android:name=".kfysts.chapter01.activity.LauncherModeSecondActivity"
android:launchMode="standard" />
按鈕的點(diǎn)擊事件:
@OnClick(R.id.btn_test1)
public void onBtnTest1Clicked() {
startActivity(new Intent(this, LauncherModeSecondActivity.class));
}
我們快速點(diǎn)擊三下按鈕,效果大概如下圖所示:
我們看下任務(wù)棧信息,在終端中輸入如下命令,將數(shù)據(jù)導(dǎo)入到log.txt中:
不了解日志重定向的同學(xué),請(qǐng)看重定向adb logcat輸出到文件.
adb shell dumpsys activity activities > log.txt
打開log.txt,我們看下任務(wù)棧信息:
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
...
* TaskRecord{1779ff5 #157 A=com.tiny.demo.firstlinecode U=0 StackId=2 sz=7}
userId=0 effectiveUid=u0a85 mCallingUid=u0a85 mUserSetupComplete=true mCallingPackage=com.tiny.demo.firstlinecode
...
Activities=[ActivityRecord{991bdbe u0 com.tiny.demo.firstlinecode/.MainActivity t157}, ActivityRecord{9eb4346 u0 com.tiny.demo.firstlinecode/.kfysts.AndroidKfystsActivity t157}, ActivityRecord{d4b56c9 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.AndroidKfystsChapter01Activity t157}, ActivityRecord{ce38d30 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeEntryActivity t157}, ActivityRecord{704de09 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeSecondActivity t157}, ActivityRecord{e063635 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeSecondActivity t157}, ActivityRecord{bd81a7a u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeSecondActivity t157}]
...
可以看到我們的LauncherModeSecondActivity
打開了三次。
使用singleTop進(jìn)行防止多次打開
小姐姐提的bug我們不能不管,針對(duì)這個(gè)bug我們第一反應(yīng)是做一個(gè)防止View的多次點(diǎn)擊
,不過這里我們有更好的選擇。針對(duì)點(diǎn)擊打開Activity
這種操作,可以使用Activity啟動(dòng)模式——singleTop
來解決,這種啟動(dòng)模式的核心就是,位于棧頂?shù)腁ctivity不會(huì)再次創(chuàng)建實(shí)例。
singTop的解釋如下:
singleTop:棧頂復(fù)用模式。
在這種模式下,如果新Activity已經(jīng)位于任務(wù)棧的棧頂,那么此Activity不會(huì)被重新創(chuàng)建,同時(shí)它的onNewIntent方法會(huì)被調(diào)用,通過此方法的參數(shù)我們可以取出當(dāng)前請(qǐng)求的信息。
如果新Activity的實(shí)例已存在但不是位于棧頂,那么新Activity仍然會(huì)重新創(chuàng)建。
話不多說,我們使用singleTop模式來優(yōu)化下,在AndroidManifest.xml文件中,給對(duì)應(yīng)的Activity添加android:launchMode="singleTop"
,代碼如下:
<activity
android:name=".kfysts.chapter01.activity.LaunchModeSingleTopTestActivity"
android:launchMode="singleTop" />
點(diǎn)擊啟動(dòng)的代碼如下:
這里新增了一條log,我們可以通過log來確定具體點(diǎn)擊了幾次。
@OnClick(R.id.btn_test5)
public void onBtnTest5Clicked() {
LogUtils.e("single Top clicked");
startActivity(new Intent(this, LaunchModeSingleTopTestActivity.class));
}
然后我們嘗試多次點(diǎn)擊按鈕,會(huì)發(fā)現(xiàn)多次打開Activity的問題已經(jīng)解決了。
先看下log,我們確實(shí)多次點(diǎn)擊了按鈕:
com.tiny.demo.firstlinecode E: single Top clicked
com.tiny.demo.firstlinecode E: single Top clicked
com.tiny.demo.firstlinecode E: single Top clicked
再看下效果圖,如下圖:
再來看下具體的任務(wù)棧信息,執(zhí)行adb shell dumpsys activity activities > log.txt
,結(jié)果如下:
* TaskRecord{b982558 #158 A=com.tiny.demo.firstlinecode U=0 StackId=3 sz=5}
userId=0 effectiveUid=u0a85 mCallingUid=u0a85 mUserSetupComplete=true mCallingPackage=com.tiny.demo.firstlinecode
...
Activities=[ActivityRecord{47ddc34 u0 com.tiny.demo.firstlinecode/.MainActivity t158}, ActivityRecord{206fabc u0 com.tiny.demo.firstlinecode/.kfysts.AndroidKfystsActivity t158}, ActivityRecord{fa98f6d u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.AndroidKfystsChapter01Activity t158}, ActivityRecord{a41b176 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeEntryActivity t158}, ActivityRecord{e2f82e5 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t158}]
從結(jié)果中可以看到,LaunchModeSingleTopTestActivity
在棧頂確實(shí)只有一個(gè)實(shí)例。
綜合以上的結(jié)果,我們可以得出結(jié)論,singleTop啟動(dòng)模式確實(shí)解決了棧頂Activity重復(fù)打開
的問題,在多次點(diǎn)擊的情況下,棧頂Activity只打開了一次。
singleTop真的能完全防止多次打開棧頂?shù)腁ctivity么?
雖然我們的bug完美解決了,但作為程序員,我們還是需要杠精下的。
這里我改下點(diǎn)擊事件的代碼,瞬間多次調(diào)用啟動(dòng)activity的代碼,代碼如下:
@OnClick(R.id.btn_test6)
public void onBtnTest6Clicked() {
for (int i = 0; i < 5; i++) {
startActivity(new Intent(this, LaunchModeSingleTopTestActivity.class));
}
}
然后我們看下效果:
納尼,竟然打開了多個(gè)。
再看下任務(wù)棧信息:
* TaskRecord{f8d39d #159 A=com.tiny.demo.firstlinecode U=0 StackId=4 sz=9}
userId=0 effectiveUid=u0a85 mCallingUid=u0a85
...
Activities=[ActivityRecord{9ed78cc u0 com.tiny.demo.firstlinecode/.MainActivity t159}, ActivityRecord{be3aa3c u0 com.tiny.demo.firstlinecode/.kfysts.AndroidKfystsActivity t159}, ActivityRecord{1654b04 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.AndroidKfystsChapter01Activity t159}, ActivityRecord{a9084f6 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LauncherModeEntryActivity t159}, ActivityRecord{932877f u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t159}, ActivityRecord{c4238aa u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t159}, ActivityRecord{e751011 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t159}, ActivityRecord{fe1ffe4 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t159}, ActivityRecord{c0d8413 u0 com.tiny.demo.firstlinecode/.kfysts.chapter01.activity.LaunchModeSingleTopTestActivity t159}]
可以看到,我們的LaunchModeSingleTopTestActivity
確實(shí)打開了五次。
其實(shí)這也很好理解,不管任何操作都是需要時(shí)間去執(zhí)行的,我們的activity的啟動(dòng)過程也是。
即使我們給Activity設(shè)置了啟動(dòng)模式,他們也不是立刻生效的,也需要執(zhí)行到對(duì)應(yīng)的代碼邏輯后才會(huì)生效。
所以如果我在for循環(huán)里面瞬間執(zhí)行多次打開Activity的操作,那么啟動(dòng)模式生效的代碼還未執(zhí)行到,所以啟動(dòng)模式就不會(huì)生效。
當(dāng)然了,代碼中一般也不會(huì)這么寫,知其然并知其所以然才是我們的目的。
github項(xiàng)目地址:Android_Base_Demo
具體頁(yè)面打開路徑:
參考
Android開發(fā)藝術(shù)探索