薛海龍,陳 渝,雷 蕾,王 丹
1.北京工業(yè)大學(xué) 計(jì)算機(jī)學(xué)院,北京 100124
2.清華大學(xué) 計(jì)算機(jī)學(xué)院,北京 100084
Android系統(tǒng)以其突出的市場占有率和開放的特性吸引了大量的開發(fā)人員,上百萬類型豐富而且創(chuàng)意獨(dú)特的應(yīng)用程序應(yīng)運(yùn)而生。然而,用戶在享受豐富多彩應(yīng)用的同時(shí),較差的響應(yīng)性能一直影響著使用體驗(yàn)。例如,應(yīng)用的啟動(dòng)時(shí)間過長,不能在短時(shí)間內(nèi)迅速對(duì)用戶輸入進(jìn)行反饋等。在功能相似的Android應(yīng)用中,響應(yīng)性能差的往往最先被淘汰。面對(duì)競爭如此激烈的Android應(yīng)用市場,開發(fā)人員迫切需要研制有效的手段來解決這個(gè)問題。
Android系統(tǒng)的程序開發(fā)框架使用單線程模型來處理用戶輸入事件,應(yīng)用啟動(dòng)時(shí),系統(tǒng)創(chuàng)建一個(gè)UI線程來運(yùn)行應(yīng)用程序,這個(gè)線程主要負(fù)責(zé)處理生命周期事件、用戶輸入事件和顯示更新事件。所有的事件默認(rèn)情況下在UI線程中順序執(zhí)行。因此,響應(yīng)性能低下的原因往往是由于在UI線程執(zhí)行耗時(shí)操作導(dǎo)致的,例如磁盤I/O、數(shù)據(jù)庫的讀取或者網(wǎng)絡(luò)訪問等,這些操作會(huì)導(dǎo)致UI線程阻塞,使得顯示不能及時(shí)更新,或者用戶陷入長時(shí)間的等待中。為避免出現(xiàn)這個(gè)問題,移動(dòng)開發(fā)采用了異步編程技術(shù),將這些耗時(shí)操作放到UI線程以外的工作線程中去執(zhí)行。由于Android提供了多種異步編程模型供開發(fā)人員使用,例如HandlerThread、IntentService、AsyncTask、ThreadPool以及新建Thread類來執(zhí)行異步操作。因此,使用異步編程技術(shù)可在一定程度上提升系統(tǒng)性能。
然而,從異步編程模型的特點(diǎn)可知,采用該模型的程序往往會(huì)在多條線程中異步執(zhí)行,使得它的執(zhí)行過程變得十分復(fù)雜。如果使用了這些編程模型,性能卻沒有達(dá)到預(yù)期的效果,開發(fā)人員就很難找到性能異常的根源所在。因此需要研究幫助開發(fā)人員分析程序在多條線程中的執(zhí)行情況的有效方法。
2012年,微軟的Ravindranath等人為了幫助Windows手機(jī)應(yīng)用開發(fā)人員了解相應(yīng)應(yīng)用在真實(shí)使用過程中的性能瓶頸和運(yùn)行失敗原因,提出了一種在真實(shí)使用過程中跟蹤應(yīng)用性能的檢測(cè)分析方法[1]。該方法關(guān)注以用戶對(duì)UI的操作為開始節(jié)點(diǎn),以由操作觸發(fā)應(yīng)用程序中的所有同步和異步任務(wù)處理完成為結(jié)束節(jié)點(diǎn),為識(shí)別異步處理任務(wù)的性能瓶頸還需要正確跟蹤跨異步邊界處理的因果關(guān)系。在實(shí)現(xiàn)上,該方法首先確定構(gòu)建用戶事務(wù)的執(zhí)行跟蹤所需的全部信息,包括UI操作事件信息、線程執(zhí)行信息、異步調(diào)用因果關(guān)系、線程同步信息、UI更新信息以及未處理的異常[2]。然后通過引入兩個(gè)自定義的庫,對(duì)Applications層App的二進(jìn)制代碼進(jìn)行動(dòng)態(tài)插樁,向開發(fā)人員展示異步程序執(zhí)行的全部流程,最終構(gòu)建出程序執(zhí)行的關(guān)鍵路徑,給開發(fā)人員指向可能導(dǎo)致響應(yīng)性能異常的根源。
利用這種方法,Ravindranath等人開發(fā)了App-Insight[3],它準(zhǔn)確地向開發(fā)人員展示了異步程序執(zhí)行的整體流程,并構(gòu)建出程序執(zhí)行關(guān)鍵路徑幫助開發(fā)人員識(shí)別性能瓶頸和異常根源。AppInsight是基于Windows編程框架中的指定接口實(shí)現(xiàn)的,因此只能對(duì)Windows平臺(tái)上的應(yīng)用進(jìn)行性能監(jiān)測(cè)與分析,并不能直接應(yīng)用到Android應(yīng)用性能的監(jiān)測(cè)分析上。
Facebook公司在這個(gè)方法的基礎(chǔ)上,針對(duì)于Android編程框架的特點(diǎn),開發(fā)出了一套遠(yuǎn)程性能監(jiān)控工具,用于對(duì)自己所開發(fā)的應(yīng)用在真實(shí)用戶的使用情況進(jìn)行監(jiān)測(cè)[4]。Facebook官方稱利用此方法確實(shí)解決了一些性能問題。
然而,以上對(duì)App的二進(jìn)制源碼進(jìn)行動(dòng)態(tài)插樁的辦法只能作用于App自身,即只有被測(cè)的App啟動(dòng)之后,才可以監(jiān)測(cè)用戶的行為操作得到插入的Log,App啟動(dòng)過程中的異步性能瓶頸是沒辦法檢測(cè)到的。針對(duì)這個(gè)問題,本文提出對(duì)Android系統(tǒng)的Framework層進(jìn)行靜態(tài)插樁來捕捉異步任務(wù)處理的各項(xiàng)信息,以監(jiān)測(cè)到App啟動(dòng)之后,并覆蓋到App啟動(dòng)過程中的所有異步執(zhí)行流程。本文的主要貢獻(xiàn):
(1)提出在Framework層進(jìn)行靜態(tài)插樁對(duì)應(yīng)用程序?qū)拥乃挟惒饺蝿?wù)的執(zhí)行進(jìn)行的跟蹤方法。該方法可以有針對(duì)性地在Framework層的API接口中找到其對(duì)應(yīng)的關(guān)鍵代碼,通過一次插樁就可以解決所有應(yīng)用程序的監(jiān)測(cè),相比較對(duì)應(yīng)用程序的插樁是更輕量級(jí)的,減輕了性能分析的復(fù)雜性。
(2)在實(shí)現(xiàn)策略上,本文通過對(duì)異步任務(wù)上的所有事務(wù)信息進(jìn)行插樁后,找到處理流程的一條關(guān)鍵路徑。該路徑上包括觸發(fā)用戶事務(wù)的關(guān)鍵事件。如果存在異步任務(wù)的處理會(huì)有新線程開始的時(shí)間戳,之后就是異步任務(wù)開始處理的過程,處理完成后會(huì)通過Handler發(fā)送消息給主線程進(jìn)行界面的刷新。通過對(duì)關(guān)鍵路徑的分析,可幫助開發(fā)人員找到應(yīng)用程序的異步任務(wù)性能瓶頸。
Android的系統(tǒng)架構(gòu)和其操作系統(tǒng)一樣,采用了分層的架構(gòu),分為4層,從高層到低層分別是應(yīng)用程序?qū)?、?yīng)用程序框架層(Framework)、系統(tǒng)運(yùn)行庫層和Linux核心層[5],如圖1所示。
Framework層提供給Android開發(fā)人員一系列的服務(wù)和API的接口,然后將一些基本功能實(shí)現(xiàn),通過接口提供給上層調(diào)用,可以重復(fù)調(diào)用。應(yīng)用程序?qū)拥乃蠥pp都是通過調(diào)用Framework層的各種API來實(shí)現(xiàn)業(yè)務(wù)需求的。
Android程序的大多數(shù)代碼操作都必須在主線程執(zhí)行,例如系統(tǒng)事件、輸入事件、程序回調(diào)服務(wù)、UI繪制等。那么在上述事件或者方法中插入的代碼也將在主線程中執(zhí)行。一旦在主線程里面添加了操作復(fù)雜的代碼,這些代碼會(huì)影響應(yīng)用程序的響應(yīng)性能。因此,為了提高用戶體驗(yàn)或者避免ANR(application is not responding)通常都會(huì)把一些耗時(shí)的任務(wù)放在子線程中去處理。
對(duì)于異步執(zhí)行的任務(wù),不需要等待返回結(jié)果,而是等任務(wù)處理完成后,再通知界面刷新。在Android中開啟異步任務(wù)的方式主要有Thread、HandlerThread、IntentService、AsyncTask和ThreadPool這5種[6],開發(fā)人員可以針對(duì)不同的應(yīng)用場景選擇不同的異步處理方式。
子線程和主線程的消息傳遞使用Android中的異步消息處理機(jī)制:Handler。Handler是谷歌封裝好的一個(gè)消息處理接口,它可以綁定在主線程或者子線程中,當(dāng)異步任務(wù)處理完成后可以利用Handler發(fā)送一個(gè)消息出去,主線程接收到這個(gè)消息之后就說明異步任務(wù)已經(jīng)執(zhí)行完成了進(jìn)而可以刷新界面,這就實(shí)現(xiàn)了異步消息的傳遞[7]。
本文通過分析用戶事務(wù)期間所對(duì)應(yīng)的關(guān)鍵路徑來判斷性能瓶頸。為方便敘述,下面先給出這些術(shù)語的定義。
定義1(用戶事務(wù))用戶事務(wù)指用戶對(duì)UI的操作開始,并完成操作觸發(fā)的所有異步任務(wù)結(jié)束。例如在圖2中,用戶事務(wù)從onClick事件開始,并在UI update事件結(jié)束。
定義2(關(guān)鍵路徑)關(guān)鍵路徑是指用戶事務(wù)中的瓶頸路徑,從而更改關(guān)鍵路徑的任何部分的長度將改變用戶感知的持久性。關(guān)鍵路徑以用戶操縱事件開始,并以UI更新事件結(jié)束。在圖2中,從①到⑦的整個(gè)路徑構(gòu)成事務(wù)的關(guān)鍵路徑。
Fig.1 Android system structure圖1 Android系統(tǒng)結(jié)構(gòu)
Fig.2 Critical path圖2 關(guān)鍵路徑
本文通過對(duì)這條關(guān)鍵路徑上的節(jié)點(diǎn)所對(duì)應(yīng)Framework層中的源碼進(jìn)行插樁來確定每個(gè)節(jié)點(diǎn)運(yùn)行的時(shí)間點(diǎn)和整個(gè)用戶事務(wù)的運(yùn)行時(shí)間,并通過Logcat輸出插樁信息。
鑒于Android自帶的Logcat功能非常強(qiáng)大,利用它可以得到插樁Log,并通過進(jìn)程號(hào)和一些篩選條件可以過濾掉大部分的無關(guān)Log,然后通過打印Log所在的線程ID,可以一目了然地得到用戶事務(wù)的關(guān)鍵路徑。
本文對(duì)應(yīng)用程序的迭代過程中的所有用戶事務(wù)構(gòu)建關(guān)鍵路徑,并對(duì)關(guān)鍵路徑的運(yùn)行時(shí)間進(jìn)行對(duì)比分析。如果某個(gè)版本的一個(gè)用戶事務(wù)的運(yùn)行時(shí)間明顯過長,可以確定此處是有問題,即是性能瓶頸,然后把這個(gè)問題反饋給開發(fā)人員,讓開發(fā)人員對(duì)代碼進(jìn)行優(yōu)化。
由于關(guān)鍵路徑以用戶操縱事件開始,并以UI更新事件結(jié)束,下面介紹事件的插樁。
Android中點(diǎn)擊事件分為4類:(1)匿名內(nèi)部類;(2)自定義事件監(jiān)聽類;(3)Activity繼承View.On-ClickListener,由 Activity 實(shí)現(xiàn)OnClick(View view)方法;(4)在XML文件中顯示指定按鈕的onClick屬性,這樣點(diǎn)擊按鈕時(shí)會(huì)利用反射的方式調(diào)用對(duì)應(yīng)Activity中的 Click()方法[8]。
無論哪種寫法,經(jīng)過分析源碼,其最終都會(huì)調(diào)用Framework中View.java文件中的performClick函數(shù),因此在這個(gè)位置插樁就可以得到點(diǎn)擊事件觸發(fā)和結(jié)束的臨界點(diǎn),如圖2中的①和③所示。
Fig.3 Handler dispatch message process圖3 Handler處理消息流程
Android中異步消息處理使用最廣泛的就是Handler機(jī)制,Handler處理消息的流程如圖3所示,包括以下4個(gè)要素。(1)Message:消息,理解為線程間通訊的數(shù)據(jù)單元;(2)Message Queue:消息隊(duì)列,用來存放通過Handler發(fā)布的消息,按照先進(jìn)先出執(zhí)行;(3)Handler:Handler是Message的主要處理者,負(fù)責(zé)將Message添加到消息隊(duì)列以及對(duì)消息隊(duì)列中的Message進(jìn)行處理;(4)Looper:循環(huán)器,扮演Message Queue和Handler之間橋梁的角色,循環(huán)取出Message Queue里面的Message,并交付給相應(yīng)的Handler進(jìn)行處理[9]。
經(jīng)過對(duì)Handler源碼的分析,確定對(duì)于異步消息處理的插樁需要分為兩部分:子線程的sendMessage和主線程的handlerMessage,找到這個(gè)關(guān)系就可以確定主線程和子線程的因果關(guān)系。
4.2.1 sendMessage
在應(yīng)用層子線程發(fā)送消息主要有send和post兩種方式[10],其中send包括sendEmptyMessage(int what)、sendEmptyMessageAtTime(int what,long uptimeMillis)、sendEmptyMessageDelayed(int what,long delayMillis)、sendMessage(Message msg)4 種;而 post包括 post(Runnable)、postAtTime(Runnable long)和 postDelayed(Runable long)3種。跟蹤這幾種方法的調(diào)用棧發(fā)現(xiàn)最終調(diào)用的會(huì)是sendMessageAtTime和send-MessageAtFrontOfQueue其中一個(gè)函數(shù),而這兩個(gè)函數(shù)最終返回的都是enqueueMessage,因此選擇在enqueueMessage的關(guān)鍵位置進(jìn)行插樁,得到如圖2中的⑥。
4.2.2 handlerMessage
應(yīng)用層處理消息是在綁定Handler的線程中進(jìn)行的,利用handler的handlerMessage處理MessageQueue中的消息,handlermessage最終都會(huì)調(diào)用Framework層的dispatchMessage[11],因此選擇在dispatchMessage的關(guān)鍵位置進(jìn)行插樁,得到如圖2所示的⑦。
Android中開啟子線程主要有Thread、Handler-Thread、IntentService、AsyncTask和ThreadPool 5種方式,針對(duì)不同的應(yīng)用場景開發(fā)人員可以選擇不同的開啟方式。其中AsyncTask和ThreadPool都是開啟一個(gè)線程池來處理異步任務(wù),而這種方式開啟子線程其實(shí)就是封裝了一個(gè)Thread,因此這3種方式可以歸為一種來討論。以上5種方式可以分為3類:Thread、HandlerThread和IntentService。
4.3.1 Thread
Android用Thread開啟子線程的方式和Java中的相似,有繼承Thread和實(shí)現(xiàn)Runnable接口兩種方式,Runnable接口里只有一個(gè)無參無返回值的run()方法,而Thread類也是實(shí)現(xiàn)了Runnable接口并重寫了run()方法,因此主要分析Thread類來找關(guān)鍵位置插樁。
開發(fā)人員在應(yīng)用層start開啟子線程是調(diào)用的libcore庫中Thread類的start方法,接著轉(zhuǎn)調(diào)VMThread的native方法create,然后轉(zhuǎn)到native層去創(chuàng)建子線程并設(shè)置屬性,創(chuàng)建成功后調(diào)用Thread類的run方法執(zhí)行開發(fā)人員自定義的異步任務(wù)[12]。在這個(gè)創(chuàng)建的過程中,需要關(guān)注的有兩個(gè)地方:線程start和線程run。線程start是主線程調(diào)用的,這就是子線程開啟的始點(diǎn),如圖2所示的②。線程run的過程是子線程處理異步任務(wù)的過程,找到子線程run的起始點(diǎn),就得到了如圖2所示的④和⑤。
4.3.2 HandlerThead
HandlerThread本質(zhì)上是一個(gè)Thread對(duì)象,只不過其內(nèi)部創(chuàng)建了該線程的Looper和MessageQueue[13]。開發(fā)人員使用HandlerThread很簡單,只需要新建一個(gè)對(duì)象,讓它start,并在handlerMessage中處理異步任務(wù)。
分析源碼知道這里調(diào)用的start仍然會(huì)調(diào)用是Thread的start方法來創(chuàng)建新的線程,但是創(chuàng)建完成之后不會(huì)調(diào)用Thread的run方法,而直接調(diào)用Handler-Thread的run方法,因此需要在HandlerThread的run方法里插樁來分辨開啟的是一個(gè)HandlerThread。它處理異步任務(wù)是在handlerMessage中進(jìn)行的,這就可以利用之前消息處理的插樁結(jié)合線程ID得到它處理異步任務(wù)的臨界點(diǎn)。
4.3.3 IntentService
IntentService 是Looper、Handler、Service的集合體[14],IntentService是繼承于Service并處理異步請(qǐng)求的一個(gè)類,在IntentService內(nèi)有一個(gè)工作線程來處理耗時(shí)操作,啟動(dòng)IntentService的方式和啟動(dòng)傳統(tǒng)Service一樣。IntentService可以自動(dòng)開啟一個(gè)Handle-Thread,并自動(dòng)調(diào)用IntentService中的onHandleIntent方法來處理異步任務(wù)。
既然IntentService是開啟了一個(gè)HandlerThread,那之前插的樁都可以直接使用,只是IntentService處理異步任務(wù)和之前的方式都不同,它是自定義了一個(gè)onHandleIntent方法,因此捕捉它處理異步任務(wù)的臨界點(diǎn)就需要在這個(gè)方法里插樁。
Android中的任何一個(gè)布局、任何一個(gè)控件其實(shí)都是直接或間接繼承自View實(shí)現(xiàn)的,當(dāng)然自定義控件也不例外,因此這些View應(yīng)該都具有相同的繪制流程與機(jī)制才能顯示到屏幕上。經(jīng)過總結(jié)發(fā)現(xiàn)每一個(gè)View的繪制過程都必須經(jīng)歷3個(gè)最主要的過程,也就是measure、layout和draw[15]。而整個(gè)View樹的繪圖流程是在ViewRootImpl類的performTraversals方法開始的,監(jiān)測(cè)界面刷新的流程需要在perform-Traversals處插樁。
為了測(cè)試此方法的可行性和有效性,本文設(shè)計(jì)了相關(guān)的測(cè)試用例,并在實(shí)際Android項(xiàng)目中使用。
5.1.1 軟件環(huán)境
本文的所有實(shí)驗(yàn)都在OPENTHOS系統(tǒng)上完成。OPENTHOS是將Android 5.1原生系統(tǒng)移植到PC端并實(shí)現(xiàn)多窗口、多任務(wù)等一系列功能的開源操作系統(tǒng),是由清華大學(xué)、清華同方和一銘公司共同聯(lián)合開發(fā)的。
5.1.2 硬件環(huán)境
把OPENTHOS系統(tǒng)安裝在清華同方T45筆記本上進(jìn)行實(shí)驗(yàn)與分析。T45的配置如下:
CPU,Intel酷睿i56200U;內(nèi)存,4 GB;磁盤,500 GB;顯卡,2 GB獨(dú)立顯卡。
在上文已經(jīng)介紹過,在Android異步編程模型中有5種開啟子線程的方式,為了驗(yàn)證本文插樁方案的有效性,需要對(duì)每一種進(jìn)行測(cè)試,基于此編寫了一個(gè)測(cè)試用例。本文設(shè)計(jì)的這個(gè)測(cè)試用例的主要功能是根據(jù)一個(gè)圖片的網(wǎng)絡(luò)地址,開啟一個(gè)子線程下載這個(gè)圖片并通知主線程刷新界面,將圖片顯示到界面上,分別用 Thread、HandlerThread、IntentService、AsyncTask、ThreadPool實(shí)現(xiàn)這個(gè)功能,通過插樁得到異步任務(wù)處理的關(guān)鍵路徑。圖4所示就是一條關(guān)鍵路徑,其中前兩列是日期和時(shí)間,第3列是進(jìn)程ID,第4列是線程ID、第5列和第6列是插樁的Log標(biāo)簽,第7列是主要的Log信息。
Fig.4 Critical path of Thread圖4 Thread的關(guān)鍵路徑
如圖4是Thread的關(guān)鍵路徑,第1行和第4行是用戶點(diǎn)擊事件的臨界點(diǎn),對(duì)應(yīng)圖2的①和③;第3行是主線程開啟子線程時(shí)調(diào)用的Thread的start方法,對(duì)應(yīng)的是圖2的②;第5行和第9行是子線程處理耗時(shí)任務(wù)的臨界點(diǎn),對(duì)應(yīng)圖2的④和⑤;第6行是子線程處理完異步消息后向主線程發(fā)送消息通知主線程刷新界面,對(duì)應(yīng)圖2的⑥;第7、8、10、11行是主線程處理子線程發(fā)來的消息并進(jìn)行界面刷新的過程,對(duì)應(yīng)圖2的⑦。
圖5是HandlerThread的關(guān)鍵路徑,它與Thread的最大區(qū)別就是子線程在dispatchMessage中處理異步任務(wù),所以第4行的Log會(huì)顯示這是一個(gè)Handler-Thread,它的異步任務(wù)是在第7行和第8行的地方處理的。
圖6是IntentService的關(guān)鍵路徑,上文提到它其實(shí)是封裝了一個(gè)HandlerThread,因此對(duì)Handler-Thread的插樁也存在,而且IntentService是在onHandleIntent中處理異步任務(wù),通過對(duì)onHandleIntent的插樁得到圖6的第8條和第12條Log。
Fig.5 Critical path of HandlerThread圖5 HandlerThread的關(guān)鍵路徑
Fig.6 Critical path of IntentService圖6 IntentService的關(guān)鍵路徑
圖7和圖8分別是AsyncTask和ThreadPool的關(guān)鍵路徑,這兩種異步處理的方式和Thread是一致的,因此得到的關(guān)鍵路徑和Thread無很大的區(qū)別。
Fig.7 Critical path ofAsyncTask圖7 AsyncTask的關(guān)鍵路徑
Fig.8 Critical path of ThreadPool圖8 ThreadPool的關(guān)鍵路徑
得到以上關(guān)鍵路徑之后,為了對(duì)比當(dāng)異步處理出現(xiàn)問題影響響應(yīng)性能的實(shí)例,讓子線程都sleep 2 s,這樣得到的關(guān)鍵路徑如圖9所示,可以發(fā)現(xiàn)第5行開始子線程處理異步任務(wù),第7條處理完成的時(shí)間比圖4延長了1.993 s,這就說明這次的異步處理任務(wù)是有性能問題的(其余的幾種異步方式結(jié)果都是類似的,就不再贅述)。
Fig.9 Thrad performance exception圖9 Thread性能異常
OPENTHOS在開發(fā)的過程中一直碰到Start-Menu卡頓的問題,尤其是在安裝的幾十個(gè)應(yīng)用的時(shí)候,點(diǎn)擊StartMenu之后需要2 s左右才能顯示出來,為了分析這個(gè)問題利用本文的方法在OPENTHOS中進(jìn)行插樁并分析原因。
本文對(duì)初期版本和近期的分別進(jìn)行分析,在對(duì)初期版本進(jìn)行插樁分析的時(shí)候發(fā)現(xiàn)StartMenu啟動(dòng)的時(shí)候并沒有開啟子線程,也就是說啟動(dòng)的過程中所有的任務(wù)都是在主線程完成的。帶著這個(gè)問題對(duì)StartMenu的源碼進(jìn)行了分析,發(fā)現(xiàn)在啟動(dòng)的過程中是需要查詢數(shù)據(jù)庫把所有已經(jīng)安裝的應(yīng)用顯示出來的,而查詢數(shù)據(jù)庫這樣耗時(shí)的任務(wù)在主線程執(zhí)行,勢(shì)必會(huì)影響性能。
接下來,對(duì)近期的版本進(jìn)行分析,插樁得到的Log信息如圖10所示,第3行是子線程處理耗時(shí)任務(wù),即查詢數(shù)據(jù)庫的開始點(diǎn),第5行是結(jié)束點(diǎn),但是可以發(fā)現(xiàn)這本應(yīng)該是一個(gè)時(shí)間段,結(jié)果卻顯示是一個(gè)時(shí)間點(diǎn),而且子線程只發(fā)送了一條消息就結(jié)束了,這絕對(duì)是不應(yīng)該的。帶著這樣的問題,分析源碼并梳理StartMenu的邏輯關(guān)系,發(fā)現(xiàn)啟動(dòng)的時(shí)候開啟的子線程仍然沒有處理耗時(shí)任務(wù),而只是進(jìn)行了一個(gè)消息的傳遞,這和初期的版本區(qū)別并不大。因此和開發(fā)人員一起重新整理StartMenu的邏輯流程,對(duì)代碼進(jìn)行調(diào)整,得到最新的版本。如圖11所示,這樣耗時(shí)的任務(wù)就在子線程中執(zhí)行,性能也提升了不少。
Fig.10 StartMenu analysis 1圖10 StartMenu分析1
Fig.11 StartMenu analysis 2圖11 StartMenu分析2
(1)對(duì)Android的Framework層進(jìn)行插樁,把插樁得到的Log信息保存下來進(jìn)行離線分析,以發(fā)現(xiàn)異步處理任務(wù)的關(guān)鍵路徑,可以幫助移動(dòng)應(yīng)用程序的開發(fā)人員監(jiān)控和診斷他們的應(yīng)用程序的性能。
(2)對(duì)Framework層進(jìn)行插樁是輕量級(jí)的,只需要一次插樁就可以獲得關(guān)鍵路徑,有更好的通用性,對(duì)于迭代發(fā)展的版本之間的性能對(duì)比尤其明顯。
(3)對(duì)Framework層進(jìn)行插樁分析可以找到程序中性能瓶頸的位置,并向開發(fā)人員提供反饋。