黃鐘銳
摘 ?要:Android操作系統(tǒng)中應(yīng)用的Activity組件有4種啟動(dòng)模式(Launch Mode)。根據(jù)不同的啟動(dòng)模式,其Activity組件在Back stack內(nèi)的行為和活動(dòng)的Activity之間跳轉(zhuǎn)的行為會(huì)有所區(qū)別。因此,其啟動(dòng)模式會(huì)影響應(yīng)用的性能(這里的性能是基于電量消耗、CPU占用率和內(nèi)存使用得到的數(shù)據(jù))。在實(shí)驗(yàn)時(shí)通過(guò)用馬爾可夫鏈來(lái)建立一個(gè)虛擬的應(yīng)用模型,對(duì)其中的啟動(dòng)模式進(jìn)行調(diào)整,記錄性能的狀態(tài)并對(duì)結(jié)果進(jìn)行對(duì)比,在這之后,得出一個(gè)基于性能的應(yīng)用的Activity啟動(dòng)模式的優(yōu)化建議。
關(guān)鍵詞:Android;啟動(dòng)模式;性能測(cè)試
中圖分類號(hào):TP31 ? ? ? ? ? ? ? ? ? 文獻(xiàn)標(biāo)志碼:A
0 引言
在通常的Android應(yīng)用程序當(dāng)中,許多的行為是依賴于Activity組件才能正常運(yùn)行的,而這些Activity的加載和存儲(chǔ)會(huì)不可避免地占用時(shí)間和系統(tǒng)資源。實(shí)際上,在每個(gè)Activity組件的設(shè)置中都有對(duì)其啟動(dòng)模式的調(diào)整,這些啟動(dòng)模式的作用會(huì)在當(dāng)前Activity發(fā)生變化時(shí)體現(xiàn)出來(lái)——不同的啟動(dòng)模式會(huì)影響B(tài)ack Stack中的Activity的信息保留情況和自動(dòng)新建Back Stack的條件。如果啟動(dòng)模式不做調(diào)整,隨著用戶啟動(dòng)過(guò)的Activity的數(shù)量越來(lái)越多,Back stack中會(huì)有更多不活躍的Activity存儲(chǔ),占用和消耗系統(tǒng)資源。因此,合理地使用和調(diào)整啟動(dòng)模式有利于緩解Back stack的存儲(chǔ)壓力。因此,該文將會(huì)討論啟動(dòng)模式的具體工作方式并基于馬爾可夫鏈設(shè)計(jì)的Android應(yīng)用模型來(lái)模擬這一過(guò)程。
1 啟動(dòng)模式的介紹
在常見的Android應(yīng)用中,通常采用給Activity設(shè)置啟動(dòng)模式的方式來(lái)控制其生成實(shí)例的具體行為以及在Back Stack中的儲(chǔ)存。
1.1 Standard(STD)
這是Android里默認(rèn)的啟動(dòng)模式,在STD的模式下,新的Intent都會(huì)被放到Back Stack的棧頂。并且不會(huì)創(chuàng)建新的任務(wù)(Task),即保持在當(dāng)前的Back Stack中操作。這意味著,在該啟動(dòng)模式下的Activity內(nèi)進(jìn)行返回操作,系統(tǒng)會(huì)將Back stack頂層的Activity移除,也就是退回到生成這個(gè)Intent的Activity內(nèi)。
1.2 singleTop(STP)
如果某個(gè)Activity的啟動(dòng)模式設(shè)置為singleTop,那么之后的操作也不會(huì)涉及創(chuàng)建新的Task和生成新的Back Stack。不過(guò)在將Activity存儲(chǔ)到Back Stack之前,會(huì)先檢查棧頂?shù)腁ctivity。如果這個(gè)Activity也是同樣的Activity的實(shí)例,那么接下來(lái)就不會(huì)創(chuàng)建新的Activity并放到棧頂,而是直接返回棧頂?shù)腁ctivity。這意味著不管在棧頂創(chuàng)建多少次一樣的singleTop類型的Activity,只要進(jìn)行一次返回操作,Back stack的狀態(tài)就會(huì)倒回到這一系列的操作之前。在設(shè)置Intent的Flag值時(shí),通過(guò)將Flag的值設(shè)置為FLAG_ACTIVITY_SINGLE_TOP即可得到和xml里聲明singleTop相同的行為。
1.3 singleTask(STK)
singleTask會(huì)使得該Back Stack中只存在一個(gè)該Intent的Activity——如果Back Stack中沒(méi)有與之相同的Intent,那么新的Activity會(huì)壓到到棧頂,反之,singleTask的Activity會(huì)使得Back Stack中具有相同Intent標(biāo)識(shí)的Activity以上的內(nèi)容都被從中銷毀。用這種機(jī)制來(lái)保證在一個(gè)Task中只有一個(gè)相同的實(shí)例。而如果在第一種情況下進(jìn)行返回操作,也就是沒(méi)有其他相同Intent的Activity的情況下,那么Back Stack中仍然保留有原先的Activity,可以倒回到進(jìn)行操作之前的狀態(tài)。而在第二種情況下,因?yàn)樵鹊腁ctivity在Back Stack中已經(jīng)被銷毀,所以此時(shí)進(jìn)行返回操作,會(huì)直接退出應(yīng)用,因?yàn)锽ack stack為空,所以在設(shè)置Intent的Flag值時(shí),通過(guò)將Flag的值設(shè)置為FLAG_ACTIVITY_NEW_TASK即可得到和xml里聲明singleTask相同的行為。
1.4 singleInstance(SIT)
如果Activity設(shè)置成這種模式,那么新建的Activity會(huì)單獨(dú)占一個(gè)Task——同時(shí)新建一個(gè)僅僅包含新Activity的Back Stack。而任何新的Back Stack會(huì)維持在僅有一個(gè)Activity的狀態(tài)。但是,特殊的是,如果在singleinstance中的Activity進(jìn)行返回操作,會(huì)導(dǎo)致Task也隨之切換——從新的Task退回原先的Task的棧頂。在遠(yuǎn)棧頂進(jìn)行返回操作無(wú)法跳轉(zhuǎn)至新的Task上。
2 基于馬爾可夫鏈的多種啟動(dòng)模式性能分析
2.1 設(shè)計(jì)思想
2.1.1 馬爾可夫鏈的引入
馬爾可夫鏈?zhǔn)侵笭顟B(tài)空間中一個(gè)狀態(tài)到另一個(gè)狀態(tài)的轉(zhuǎn)換的無(wú)記憶性的隨機(jī)過(guò)程。這一過(guò)程只與當(dāng)前的狀態(tài)有關(guān),和之前的過(guò)程沒(méi)有關(guān)聯(lián)。而狀態(tài)的轉(zhuǎn)移概率則是取決于當(dāng)前所處的狀態(tài)轉(zhuǎn)向其他狀態(tài)的概率。由時(shí)刻n轉(zhuǎn)向時(shí)刻n+1的跳轉(zhuǎn)概率表示方法為Pr(Xn+1=x|Xn=xn)其中Pr表示跳轉(zhuǎn)的概率,n與n+1表示第n時(shí)刻和第n+1時(shí)刻,X表示某時(shí)刻的狀態(tài),x為具體的狀態(tài)類型。 而更直觀的表示方法,如圖1所示,可以直接用有向圖在路徑上標(biāo)明概率的方式來(lái)表示馬爾可夫鏈。
類比到這里的模型中,在固定的時(shí)間內(nèi),對(duì)于每種狀態(tài),我們規(guī)定好用戶進(jìn)行各種操作的可能性,方便之后進(jìn)行模擬。而在數(shù)據(jù)結(jié)構(gòu)上,用一個(gè)xml文件來(lái)存儲(chǔ)跳轉(zhuǎn)的方向和概率,在讀入后轉(zhuǎn)換成用二維數(shù)組表示的鄰接矩陣。此時(shí)處理跳轉(zhuǎn)問(wèn)題就可以像處理其他問(wèn)題一樣來(lái)處理Activity之間的連接關(guān)系了。這里引入馬爾可夫鏈的意義不僅是在于實(shí)現(xiàn)一個(gè)跳轉(zhuǎn)的控制或者得到隨機(jī)的結(jié)論,而是希望能借助這個(gè)模型來(lái)模擬實(shí)際使用時(shí)的用戶行為。這里筆者首先選用了一組有向圖中所表示的數(shù)據(jù)來(lái)進(jìn)行測(cè)試,接下來(lái)會(huì)有一組根據(jù)筆者使用社交軟件行為的數(shù)據(jù)模擬。
2.1.2 資源占用的表示和測(cè)量
使用的測(cè)量方式是使用Google的Android Profiler來(lái)進(jìn)行應(yīng)用性能的測(cè)量。在應(yīng)用運(yùn)行時(shí),通過(guò)Android Profiler可以得到我們需要的性能指標(biāo)。而在這個(gè)例子中,我們測(cè)試的指標(biāo)是CPU使用、內(nèi)存占用和電量消耗。這樣的好處是監(jiān)控和測(cè)量消耗的資源是由電腦來(lái)提供的而不是手機(jī)端進(jìn)行資源消耗,可以將關(guān)注點(diǎn)都放在應(yīng)用本身。
2.2 模型的構(gòu)建
在準(zhǔn)備好模擬的條件之后,就要做好具體模型環(huán)境的搭建。在每個(gè)Activity啟動(dòng)時(shí),讀取xml配置文件中的數(shù)據(jù),從中提取出數(shù)據(jù)。而在另一方面,每次Activity進(jìn)行的時(shí)候都生成一個(gè)隨機(jī)數(shù),用來(lái)記錄之后要進(jìn)行的動(dòng)作。在這之后,根據(jù)xml數(shù)據(jù)進(jìn)行對(duì)應(yīng)的動(dòng)作。不同的Activity也對(duì)應(yīng)著不同的內(nèi)容和啟動(dòng)模式以及跳轉(zhuǎn)概率。
3 測(cè)試與驗(yàn)證
3.1 資源加載
如果沒(méi)有適量的資源放到應(yīng)用中進(jìn)行加載,那么資源的消耗就會(huì)變得不是很清晰。因此在設(shè)計(jì)時(shí),可以特意放一些組件和圖片音頻等資源來(lái)增加對(duì)資源加載的負(fù)荷,以此來(lái)模擬Activity在加載時(shí)需要進(jìn)行的工作。
3.2 實(shí)驗(yàn)環(huán)境
該次實(shí)驗(yàn)的所有實(shí)驗(yàn)環(huán)境采用的是Pixel 2XL的虛擬機(jī),虛擬機(jī)是通過(guò)Android Virtual Device生成的。配置列表見表1。
3.3 基于馬爾可夫鏈的啟動(dòng)模式性能分析
3.3.1 全部用TSD模式
觀察到的情況是在standard的情況下,如圖2所示。內(nèi)存的使用會(huì)出現(xiàn)一個(gè)逐漸上升的趨勢(shì)。這說(shuō)明Back Stack中仍然存儲(chǔ)著這些資源,并且在創(chuàng)建和銷毀Activity前CPU使用率和電量的消耗也會(huì)出現(xiàn)一個(gè)小的極大值。
圖中從上至下依次為CPU使用率,內(nèi)存占用量,和電量消耗速度
3.3.2 混合采用
在這種情況下,內(nèi)容比較復(fù)雜,而且行為稍有混亂,如圖3所示,在cover的Activity啟動(dòng)時(shí),電量的消耗要比其他的Activity要高一些,而cover的啟動(dòng)模式為singleTop。另一方面,CPU占用率方面,在nextcover的Activity啟動(dòng)時(shí)CPU的占用率增量要比其余的2種情況明顯??梢哉J(rèn)為是在singleTask的啟動(dòng)模式下對(duì)CPU的使用率更高。
3.4 限制
首先,在設(shè)計(jì)測(cè)試時(shí)有意忽略掉了singleInstance的情況。因?yàn)檫@種設(shè)計(jì)模式使用的情況較少,由于每次新建的Activity都會(huì)在新的Task中,singleInstance用戶的體驗(yàn)是比較不符合習(xí)慣。第二,測(cè)試時(shí)沒(méi)有引入TaskAffinity這個(gè)變量,這意味著在測(cè)試環(huán)境內(nèi)不會(huì)出現(xiàn)包名不同的Activity在同一個(gè)Back Stack中的情況,所以在測(cè)試?yán)餂](méi)有發(fā)揮出singleTask的全部功能其實(shí)際情況要遠(yuǎn)遠(yuǎn)復(fù)雜于用馬爾可夫鏈生成的簡(jiǎn)單模型,包括動(dòng)態(tài)加載的資源。該文只是提供了一種解決這類問(wèn)題的思路。另一方面,后續(xù)還應(yīng)繼續(xù)對(duì)得到的數(shù)據(jù)進(jìn)行更多定量的分析,該文只是對(duì)比了其峰值的大小,如果能有更嚴(yán)謹(jǐn)?shù)臄?shù)學(xué)方法分析這些數(shù)據(jù),一定能得到更多的信息。因此后續(xù)的研究應(yīng)當(dāng)著重于對(duì)實(shí)際情況的擬真和對(duì)獲取數(shù)據(jù)進(jìn)行更詳細(xì)處理。
4 測(cè)試結(jié)果的分析和整理
在得到監(jiān)控?cái)?shù)據(jù)之后,接下來(lái)的工作就是對(duì)數(shù)據(jù)進(jìn)行進(jìn)一步的分析和處理。這里通過(guò)Android Profiler來(lái)獲取了模擬程序運(yùn)行的100 s內(nèi)其在各層Activity上的狀態(tài)信息。由于CPU的狀態(tài)和電量消耗實(shí)時(shí)發(fā)生變化,所以在收集數(shù)據(jù)的時(shí)候更要關(guān)注每一段的極值點(diǎn),并將多次的測(cè)量數(shù)據(jù)進(jìn)行處理和統(tǒng)計(jì)。為了保持?jǐn)?shù)據(jù)的直觀性和盡可能多的保留更多的信息,在進(jìn)行統(tǒng)計(jì)時(shí)采用了箱型圖的方式保存數(shù)據(jù)。
Android Profiler 中沒(méi)給出電量消耗的縱軸單位,只是用了Light、Medium、High和Heavy來(lái)表示電量消耗的速度快慢,為了便于統(tǒng)計(jì),我們以整個(gè)運(yùn)行過(guò)程中電量消耗最快的那一點(diǎn)作為1個(gè)單位,這里記錄的為其余各時(shí)刻消耗電量和其所占的百分比。因?yàn)樵陔娏肯牡淖畲笾狄呀?jīng)達(dá)到了Heavy的級(jí)別,因此可以作為對(duì)電量消耗測(cè)量的一個(gè)“標(biāo)桿”來(lái)看待。
根據(jù)圖4與圖5中所呈現(xiàn)出的統(tǒng)計(jì)分析,從平均值來(lái)看,Standard的啟動(dòng)模式對(duì)CPU的占用要少一些,而singleTop和singleTask的啟動(dòng)模式在CPU占用上雖然平均值相差很小,但是singleTask對(duì)CPU的使用率表現(xiàn)更不穩(wěn)定。而在電量消耗方面,singleTask的表現(xiàn)非常好,平均值和中位數(shù)明顯低于另外2種,并且表現(xiàn)也比較穩(wěn)定。而Standard和singleTop的最低消耗表現(xiàn)相似,singleTop的電量消耗雖然總體表現(xiàn)更穩(wěn)定,但是最差情況非常糟糕。
5 結(jié)論
通過(guò)基于馬爾可夫鏈的隨機(jī)實(shí)驗(yàn)測(cè)試,可以明顯的觀測(cè)到,不同的啟動(dòng)模式對(duì)性能表現(xiàn)的影響是存在差異的。這也證實(shí)了實(shí)驗(yàn)過(guò)程的有效性。在一個(gè)Activity從Back Stack被移除前,其內(nèi)存的占用是不會(huì)解除的,也就是說(shuō)如果需要維持較低的內(nèi)存占用率,我們可以通過(guò)阻止新Activity產(chǎn)生以及摧毀Activity的方式來(lái)對(duì)內(nèi)存方面進(jìn)行限制。而這需要通過(guò)活用singleTop和singleTask的方式來(lái)達(dá)到這一目的。從另一個(gè)角度考量,如果我們需要的是減少CPU的占用率,避免對(duì)Back stack進(jìn)行復(fù)雜操作可以減少CPU的占用。這就應(yīng)當(dāng)在程序設(shè)計(jì)時(shí)減少singleTask和singleInstance的使用,使得應(yīng)用對(duì)棧的操作量減小至最低。
另外,由于啟動(dòng)模式可以通過(guò)在Intent中設(shè)置Flag的值來(lái)控制,這就意味著我們可以在寫程序的時(shí)候通過(guò)設(shè)置參數(shù)的方式來(lái)根據(jù)我們的需要選擇對(duì)資源分配更有利的啟動(dòng)模式。不過(guò)在設(shè)置啟動(dòng)模式時(shí)還應(yīng)該考慮到用戶的體驗(yàn)(如使用singleTask的時(shí)候可能會(huì)銷毀很多處于Back Stack中的Activity)。因此在選用時(shí)應(yīng)當(dāng)注意這點(diǎn)。而對(duì)于不是很確定的情況,可以通過(guò)調(diào)研等方式獲取用戶在使用應(yīng)用時(shí)的各種行為及發(fā)生頻率,通過(guò)構(gòu)建馬爾可夫鏈并在模型中監(jiān)控資源消耗的方式來(lái)確認(rèn)其是否合理。這給了開發(fā)者在選擇啟動(dòng)模式時(shí)一個(gè)相對(duì)直觀并可行的新思路。
參考文獻(xiàn)
[1]Taolue Chen,Jinlong He,F(xiàn)u Song,ex al.Android Stack Machine[J].LNCS 10982,2018,pp.487–504.
[2]Android documentation[S/OL].https://developer.android.com/guide/components/tasks-and-back-stack.html?hl=zh-CN.
[3]Chuangang Ren,Yulong Zhang,Hui Xue,ex al.Towards Discovering and Understanding Task Hijacking in Android[R].24th USENIX Security Symposium.2015.
[4]Norris,James R.Markov chains[M].UK:Cambridge University Press,1998.