馬思峻 肖 榮 成江偉
(上海理想信息產(chǎn)業(yè)(集團(tuán))有限公司 上海 201315)
?
Android應(yīng)用性能數(shù)據(jù)采集探針研究
馬思峻 肖 榮 成江偉
(上海理想信息產(chǎn)業(yè)(集團(tuán))有限公司 上海 201315)
從社交到電商、旅游、醫(yī)療、教育、餐飲和出行,移動(dòng)應(yīng)用逐漸成為人們?nèi)粘I畹囊徊糠?。移?dòng)應(yīng)用開發(fā)商之間的競(jìng)爭(zhēng)越來越激烈,移動(dòng)應(yīng)用想要提升用戶體驗(yàn)和增加用戶數(shù),從人們真實(shí)使用的手機(jī)上采集性能數(shù)據(jù)、崩潰日志至關(guān)重要。針對(duì)通過修改源代碼埋樁方式采集移動(dòng)應(yīng)用性能數(shù)據(jù)有諸多缺陷,提出使用面向切面編程對(duì)Android源代碼進(jìn)行預(yù)編譯處理,能夠在不需要手工修改代碼的前提下自動(dòng)插入性能數(shù)據(jù)采集探針代碼。該方法已經(jīng)在多個(gè)項(xiàng)目中實(shí)際使用,使用該方法處理過的Android應(yīng)用能夠遠(yuǎn)程上報(bào)符合要求的應(yīng)用性能和用戶使用習(xí)慣數(shù)據(jù)。證明該方法在實(shí)際使用中具有可行性,為Android應(yīng)用性能數(shù)據(jù)采集提供了新的思路。
Andriod 應(yīng)用性能監(jiān)控 面向切面編程 Java代理 ASM
隨著智能手機(jī)的迅速普及,移動(dòng)互聯(lián)網(wǎng)正在改變我們的衣食住行,許多互聯(lián)網(wǎng)公司看到了其中巨大的商機(jī),開發(fā)了各種移動(dòng)應(yīng)用滿足我們的工作和生活需求。但是移動(dòng)應(yīng)用爭(zhēng)奪用戶之間的競(jìng)爭(zhēng)空前激烈,許多移動(dòng)應(yīng)用的生存周期不到一年,一個(gè)交互的卡頓就會(huì)導(dǎo)致用戶卸載應(yīng)用。一款成功的移動(dòng)應(yīng)用不但要有突破性的功能,還要有良好的用戶體驗(yàn)。
移動(dòng)應(yīng)用上線僅僅是個(gè)開始,為了增加用戶數(shù),提高保有率和使用時(shí)間,后期有大量的運(yùn)維工作。而且Android平臺(tái)的碎片化,不可能在每家廠商各種型號(hào)的手機(jī)上做完整測(cè)試,錯(cuò)誤不可避免。所以需要從用戶當(dāng)前正在使用的手機(jī)中采集各種性能數(shù)據(jù)和崩潰日志,為市場(chǎng)營(yíng)銷和錯(cuò)誤修正工作提供分析數(shù)據(jù)。
業(yè)內(nèi)對(duì)該問題提出了很多解決方案,國(guó)內(nèi)最常用的移動(dòng)統(tǒng)計(jì)平臺(tái)是百度移動(dòng)統(tǒng)計(jì)、友盟統(tǒng)計(jì)。它們使用的方法是在每個(gè)頁(yè)面的onResume、onPause方法中插入統(tǒng)計(jì)頁(yè)面時(shí)長(zhǎng)代碼,自定義事件在應(yīng)用程序中埋點(diǎn)來統(tǒng)計(jì)用戶的點(diǎn)擊行為,但這種方式有諸多弊端:
(1) 原有代碼變更頻繁,讓插入標(biāo)記的代碼也經(jīng)常變動(dòng),非常耗時(shí)而且容易引入錯(cuò)誤,給開發(fā)工作帶來額外負(fù)擔(dān)。
(2) 插入代碼的粒度決定了采集數(shù)據(jù)的詳細(xì)程度,而且只能采集可以預(yù)測(cè)到的錯(cuò)誤。
(3) Android經(jīng)常使用多線程編程和異步請(qǐng)求,讓采集性能數(shù)據(jù)變得困難。
國(guó)外facebook工程師Delyan Kratunov撰文介紹了他們收集Android應(yīng)用性能檢測(cè)數(shù)據(jù)的新方法,核心是基于規(guī)則的字節(jié)碼重寫器。該重寫器可以匹配代碼位置,然后插入或操作代碼,要實(shí)現(xiàn)該方法的過程中會(huì)遇到一些需要克服的問題,新方法已經(jīng)使用在facebook開發(fā)的應(yīng)用中。這給了我們使用新方法的思路,不需要手動(dòng)在頁(yè)面和函數(shù)執(zhí)行前后插入代碼,而是使用面向切面編程AOP[1]方式就能在原有代碼中嵌入性能數(shù)據(jù)采集探針,采集到我們需要的數(shù)據(jù)。國(guó)內(nèi)廠商聽云等都在努力研發(fā)相關(guān)的產(chǎn)品。
本文詳細(xì)介紹了使用AOP方法實(shí)現(xiàn)Android移動(dòng)應(yīng)用性能數(shù)據(jù)采集探針的實(shí)現(xiàn)原理和相關(guān)技術(shù),架構(gòu)和組件間的關(guān)系以及關(guān)鍵技術(shù)難點(diǎn)。
Android應(yīng)用使用Java語(yǔ)言開發(fā),Java中的相關(guān)技術(shù)可以借鑒到Android開發(fā)中。Java中已經(jīng)存在面向切面編程技術(shù),可以通過預(yù)編譯方式或運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加代碼。允許分離應(yīng)用的業(yè)務(wù)邏輯和系統(tǒng)服務(wù),將日志記錄、性能統(tǒng)計(jì)、異常處理等代碼從業(yè)務(wù)邏輯代碼中劃分出來,修改這些代碼可以不影響原有業(yè)務(wù)邏輯,這給了我們?cè)贏ndroid系統(tǒng)上實(shí)現(xiàn)性能數(shù)據(jù)采集的新思路。
JDK1.5引入了Java代理技術(shù)javaagent[2],可以在main運(yùn)行前動(dòng)態(tài)替換和修改類的定義,JDK1.6增強(qiáng)了功能,在運(yùn)行時(shí)也能動(dòng)態(tài)替換和修改類。對(duì)Java class類進(jìn)行修改,有很多字節(jié)碼庫(kù)可以做選擇,常用的有javaassit、asm[3-4]。兩者相比較,javaassit是基于源碼,asm是基于字節(jié)碼,asm開發(fā)復(fù)雜,而且需要了解字節(jié)碼的知識(shí),但是性能更快、靈活性也高。不能因?yàn)橐胄阅軘?shù)據(jù)采集探針而影響原有應(yīng)用的性能,所以使用asm修改Java編譯出的class文件。
Android編譯時(shí)先使用Java編譯器把Java文件編譯成class文件,Android 4.4以前通過dex命令把class文件編譯成dex文件,Android 4.4以后把class文件編譯成art文件[5],然后打包簽名生成安裝包。在javac編譯Java源代碼生成class文件之前把Android SDK中和網(wǎng)絡(luò)請(qǐng)求、異步任務(wù)、Sqllite、Activity、異常處理相關(guān)的類替換掉,加入性能數(shù)據(jù)采集代碼,最后生成的安裝包就會(huì)自動(dòng)包含探針功能。由于Android sdk的相應(yīng)類替換掉是程序自動(dòng)運(yùn)行的,沒有必要擔(dān)心會(huì)漏掉監(jiān)測(cè)數(shù)據(jù)。
文獻(xiàn)[6]分析了該方法理論上是可行的,但在實(shí)際開發(fā)過程中會(huì)遇到許多問題。首先,許多應(yīng)用都使用了很多第三方庫(kù),除了需要替換Android SDK原有的類之外,還要替換很多第三方類庫(kù),例如網(wǎng)絡(luò)請(qǐng)求除了java.net.URL外,還有Httpclient、okHttp2、okHttp3庫(kù)[7]。其次,替換java class文件引入的應(yīng)用性能數(shù)據(jù)采集和上報(bào)功能不能導(dǎo)致應(yīng)用程序安裝包膨脹,不能影響原有應(yīng)用的業(yè)務(wù)邏輯和性能。最后要使用方便,不給開發(fā)人員增加額外負(fù)擔(dān),只需要簡(jiǎn)單的代碼添加就能引入性能數(shù)據(jù)采集功能,支持各種Android開發(fā)工具,例如Eclipse、Android Studio。
性能數(shù)據(jù)采集探針由三部分構(gòu)成:第一部分java rewriter重寫jar包,匹配類名和方法名注入替換類和替換方法;第二部分agent實(shí)現(xiàn)插入性能統(tǒng)計(jì)代碼的Android SDK、第三方庫(kù)的替換類和采集數(shù)據(jù)上報(bào)服務(wù)器;第三部分Eclipse插件[8],將探針安裝到Eclipse開發(fā)環(huán)境中。三部分包含的功能和之間的關(guān)系如圖1所示。
圖1 組件功能和相互之間的關(guān)系
3.1 Java類重寫
通過java-javaagent參數(shù)指定rewriter.jar包文件路徑,rewriter.jar在加載class文件之前對(duì)字節(jié)碼修改。jar包中的META-INF/MAINIFEST.MF文件包含Premain-Class、Agent-Class鍵值對(duì),其值為實(shí)現(xiàn)字節(jié)碼修改的類名,修改字節(jié)碼類中必須實(shí)現(xiàn)premain、agentmain方法,參照J(rèn)ava的標(biāo)準(zhǔn),在此不再展開。
常用的Android應(yīng)用開發(fā)工具有Ant、Eclipse、Maven、Android Studio。為了同時(shí)支持四種Android開發(fā)工具,需要在每種開發(fā)工具執(zhí)行編譯方法前替換掉和性能數(shù)據(jù)采集相關(guān)類的class字節(jié)碼,四種工具在編譯程序時(shí)執(zhí)行的方法是不同的,它們執(zhí)行的類名和方法如表1所示。
表1 開發(fā)工具執(zhí)行類名和方法
圖2 類字節(jié)碼替換調(diào)用流程
遵循asm開發(fā)規(guī)范,為每種開發(fā)工具建立各自的字節(jié)碼轉(zhuǎn)換適配器ClassVisitorFactory。ClassVisitorFactory中創(chuàng)建類適配器ClassAdapter,類適配器ClassAdapter中創(chuàng)建類方法轉(zhuǎn)換工廠MethodVisitorFactory,類方法轉(zhuǎn)換工廠中創(chuàng)建MethodVisitor對(duì)象,MethodVisitor對(duì)象實(shí)現(xiàn)onMethodEnter方法,onMethodEnter方法調(diào)用InvocationDispatcher類的invoke函數(shù),流程圖如圖2所示。
invoke函數(shù)負(fù)責(zé)具體替換Android SDK和第三方庫(kù)類,其中使用裝飾者(Decorator)模式,按照順序替換自定義注釋、Activity(頁(yè)面)、異步任務(wù)、類方法、Android應(yīng)用上下文,對(duì)應(yīng)的ClassVisitor分別為AnnotatingClassVisitor、ActivityClassVisitor、AsyncTaskClassVisitor、WrapMethodClassVisitor、ContextInitializationClassVisitor。
WrapMethodClassVisitor 負(fù)責(zé)替換需要采集性能數(shù)據(jù)的方法,jar中包含配置文件說明原有方法和替換方法的對(duì)應(yīng)關(guān)系和替換方式。jvm加載類時(shí)讀取方法名,方法名和配置文件進(jìn)行匹配,如果發(fā)現(xiàn)有替換的方法,就Replace或wrapper原有方法的字節(jié)碼,表2給出配置文件的一部分內(nèi)容。
表2 替換方法配置文件
Wrapper和Replace的區(qū)別在于,Wrapper是執(zhí)行完原有方法之后再執(zhí)行插入的字節(jié)代碼,而Replace是直接執(zhí)行替換代碼。
ActivityClassVisitor在Activity生命周期中的onStart、onStop函數(shù)中插入頁(yè)面計(jì)時(shí)開始、結(jié)束代碼和應(yīng)用狀態(tài)監(jiān)聽。AsyncTaskClassVisitor在AsyncTask任務(wù)執(zhí)行函數(shù)doInBackground和執(zhí)行完成函數(shù)onPostExecute中插入跟蹤計(jì)時(shí)代碼。
該jar包編譯和執(zhí)行時(shí)需要引入asm、dom4j、guava、reflections、slf4j等第三方j(luò)ar包,rewrite.jar的premain或agentmain方法執(zhí)行結(jié)束,涉及應(yīng)用性能統(tǒng)計(jì)的類和方法替換完畢,然后開發(fā)工具開始進(jìn)行Android應(yīng)用編譯。
3.2 agent代理包
Eclipse插件把a(bǔ)gent.jar包添加到Android應(yīng)用引用的庫(kù)文件中,而不是在Java啟動(dòng)時(shí)通過-javaagent參數(shù)加載,它是Android性能探針的核心。agent包含插入數(shù)據(jù)采集代碼的Android sdk和第三方庫(kù)的替換類、數(shù)據(jù)隊(duì)列預(yù)處理、數(shù)據(jù)上傳、各種參數(shù)配置等功能。
3.2.1 agent代理包架構(gòu)
代理包架構(gòu)如圖3所示。
圖3 agent代理包架構(gòu)
ActivityTrace、應(yīng)用運(yùn)行時(shí)的各種測(cè)量值、Agent自身錯(cuò)誤采集后加入任務(wù)隊(duì)列,定時(shí)任務(wù)從隊(duì)列中取出數(shù)據(jù)直接加入上傳數(shù)據(jù)對(duì)象。Http網(wǎng)絡(luò)請(qǐng)求和執(zhí)行函數(shù)時(shí)間測(cè)量值因?yàn)樾枰鰯?shù)據(jù)處理,定時(shí)器取出數(shù)據(jù)通過生產(chǎn)者消費(fèi)者模式處理后加入上傳數(shù)據(jù)對(duì)象。Http錯(cuò)誤數(shù)據(jù)不通過任務(wù)隊(duì)列直接進(jìn)入生產(chǎn)者消費(fèi)者模式處理數(shù)據(jù)。
3.2.2 數(shù)據(jù)采集
agent采集的數(shù)據(jù)有Activity打開及其內(nèi)部方法執(zhí)行時(shí)間、網(wǎng)絡(luò)請(qǐng)求等待時(shí)間、agent自身異常、應(yīng)用運(yùn)行崩潰異常日志、地理位置、CPU和內(nèi)存使用率。
(1) 方法執(zhí)行時(shí)間數(shù)據(jù)采集
以SDK中圖片加載類BitmapFactory為例進(jìn)行說明,根據(jù)配置文件替換為BitmapFactoryInstrumentation,類中傳入文件地址加載圖片的函數(shù)decodeFile代碼修改為在執(zhí)行原有功能前先執(zhí)行enterMethod進(jìn)入方法函數(shù),原有功能執(zhí)行結(jié)束后執(zhí)行exitMethod()退出方法函數(shù)。
每個(gè)函數(shù)執(zhí)行時(shí)都有其所在的上下文,不是Activity就是其他方法,enterMethod函數(shù)新建性能跟蹤子對(duì)象時(shí)傳入它所在父跟蹤對(duì)象的唯一標(biāo)識(shí)。子跟蹤對(duì)象創(chuàng)建成功后加入父跟蹤對(duì)象的子對(duì)象集合,并且給子跟蹤對(duì)象的函數(shù)進(jìn)入時(shí)間屬性設(shè)置為當(dāng)前時(shí)間,同時(shí)把子跟蹤對(duì)象賦值給ThreadLocal線程安全變量。
exitMethod函數(shù)從ThreadLocal變量中取出Trace跟蹤對(duì)象,給跟蹤對(duì)象設(shè)置函數(shù)執(zhí)行完成時(shí)間戳。然后把函數(shù)執(zhí)行完成采集到的跟蹤對(duì)象壓入非阻塞隊(duì)列,由數(shù)據(jù)預(yù)處理模塊讀取Trace跟蹤對(duì)象進(jìn)行數(shù)據(jù)預(yù)處理。
BitmapFactory中有很多返回Bitmap的重載函數(shù),在BitmapFactoryInstrumentation中全部實(shí)現(xiàn)這些重載函數(shù)。在配置文件中記錄了所有要替換的方法和其所在的類,采集GSon、JSONArray、JSONObject、AsyncTask、SQLite等功能執(zhí)行時(shí)的性能數(shù)據(jù)也采用類似的方法。
(2) Activity性能數(shù)據(jù)采集
Activity生命周期在啟動(dòng)時(shí)會(huì)觸發(fā)onCreate,在結(jié)束時(shí)觸發(fā)onDestroy。但有些情況下,例如長(zhǎng)時(shí)間不操作屏幕鎖屏和用戶按HOME鍵后應(yīng)用置于后臺(tái),也要認(rèn)為頁(yè)面已經(jīng)結(jié)束運(yùn)行。在執(zhí)行Activity的onCreate方法前調(diào)用TraceMachine的enterMethod函數(shù),用戶長(zhǎng)時(shí)間不操作或者應(yīng)用切入后臺(tái)運(yùn)行后,頁(yè)面也會(huì)結(jié)束跟蹤。然后把ActivityTrace對(duì)象壓入非阻塞隊(duì)列,由任務(wù)隊(duì)列將其加入到上傳數(shù)據(jù)對(duì)象中,ActivityTrace包含頁(yè)面的進(jìn)入和退出時(shí)間、頁(yè)面包含方法執(zhí)行消耗時(shí)間、內(nèi)存和CPU的取樣值,判斷Activity結(jié)束的流程如圖4所示。
圖4 判斷Activity結(jié)束流程
(3) 網(wǎng)絡(luò)請(qǐng)求等待時(shí)間數(shù)據(jù)采集
以Android應(yīng)用最常用的網(wǎng)絡(luò)請(qǐng)求庫(kù)Httpclient為例進(jìn)行說明,Httpclient中的網(wǎng)絡(luò)請(qǐng)求類HttpRequest和響應(yīng)類Httpresponse都需要HttpEntity屬性,創(chuàng)建包含跟蹤狀態(tài)對(duì)象的HttpEntity接口實(shí)例HttpRequestEntityImp和HttpResponseEntityImpl,賦值給HttpRequest和Httpresponse的HttpEntity屬性。Http請(qǐng)求開始時(shí),向transactionState跟蹤對(duì)象置入開始時(shí)間、請(qǐng)求地址、請(qǐng)求方法、網(wǎng)絡(luò)類型、網(wǎng)絡(luò)運(yùn)營(yíng)商。如果請(qǐng)求成功,HttpRequestEntityImpl向transactionState置入發(fā)送字節(jié)數(shù),HttpResponseEntityImpl向transactionState置入接收字節(jié)數(shù)、請(qǐng)求結(jié)束時(shí)間,返回狀態(tài)碼。如果請(qǐng)求錯(cuò)誤,置入結(jié)束時(shí)間、錯(cuò)誤碼。一個(gè)Http請(qǐng)求完成后,transactionState持有從本次Http請(qǐng)求采集到的所有數(shù)據(jù),新建對(duì)象后壓入非阻塞隊(duì)列。
Android Http請(qǐng)求有很多種實(shí)現(xiàn)方式,除了Android SDK自帶的HttpURLConnection和HttpClient(Android 6.0已經(jīng)移除)外,還有OkHttp2、Retrofit[9]等第三方庫(kù),每個(gè)第三方庫(kù)都要重寫其部分源代碼。加入性能采集代碼。以O(shè)kHttp2為例,新建實(shí)現(xiàn)Call和CallBack接口并加入transactionState統(tǒng)計(jì)對(duì)象的實(shí)例,在Http請(qǐng)求和響應(yīng)時(shí),采集本次Http請(qǐng)求的數(shù)據(jù),Http請(qǐng)求完成后新建對(duì)象壓入非阻塞隊(duì)列。
(4) 自身錯(cuò)誤采集
Agent加入原有應(yīng)用后不可避免會(huì)引入錯(cuò)誤,為了跟蹤分析錯(cuò)誤發(fā)生的原因和次數(shù),使用埋樁方式,在錯(cuò)誤可能發(fā)生的位置(例如初始化、上傳錯(cuò)誤日志時(shí))插入代碼,新建錯(cuò)誤對(duì)象計(jì)數(shù)類,設(shè)置錯(cuò)誤發(fā)生的詳細(xì)說明,發(fā)生次數(shù)等屬性,壓入非阻塞隊(duì)列。在多線程運(yùn)行環(huán)境中,更改發(fā)生錯(cuò)誤發(fā)生次數(shù)時(shí),需要使用同步鎖Synchronized[10]。
(5) 崩潰日志采集
根據(jù)文獻(xiàn)[11]的分析,在agent初始化時(shí)新建實(shí)現(xiàn)UncaughtException接口的異常捕獲類,并注冊(cè)為程序中默認(rèn)未捕獲異常處理,當(dāng)應(yīng)用出現(xiàn)沒有處理的異常時(shí),由該類來捕獲這些異常。該類將異常發(fā)生的時(shí)間、Activity的執(zhí)行軌跡、設(shè)備信息、應(yīng)用信息等以JSON格式保存在本地,每條崩潰記錄都有唯一標(biāo)識(shí)。然后另外啟動(dòng)一個(gè)線程上傳崩潰日志給服務(wù)器,上傳成功后從本地刪除已發(fā)送的日志。
(6) 位置信息采集
應(yīng)用運(yùn)營(yíng)方需要統(tǒng)計(jì)使用者的地域分布,所以在agent包中加入了上傳位置信息的功能,該功能會(huì)消耗電量,使用者可以關(guān)閉定位功能。如果使用者打開了該功能并且應(yīng)用中有定位權(quán)限,agent就開啟定位監(jiān)聽,為了不影響用戶使用,定位是采用被動(dòng)定位方式(PASSIVE_PROVIDER),使用現(xiàn)成的定位提供方,不主動(dòng)請(qǐng)求位置信息。當(dāng)其他應(yīng)用更新了定位信息,系統(tǒng)會(huì)保存,應(yīng)用接收到消息后就直接使用。
3.2.3 數(shù)據(jù)預(yù)處理
數(shù)據(jù)預(yù)處理模塊使用生產(chǎn)者、消費(fèi)者模式[12]對(duì)數(shù)據(jù)進(jìn)行處理,agent初始化時(shí)為Http請(qǐng)求、Http錯(cuò)誤、Activity跟蹤測(cè)量值、方法執(zhí)行時(shí)間、自身錯(cuò)誤等數(shù)據(jù)都建立各自的生產(chǎn)者和消費(fèi)者類。管理中心管理這些生產(chǎn)者和消費(fèi)者,可以增加和刪除這些實(shí)現(xiàn)生產(chǎn)者Producer接口和消費(fèi)者Consumer接口的類。從隊(duì)列中取出數(shù)據(jù)后,使用統(tǒng)一的生產(chǎn)者接口方法對(duì)數(shù)據(jù)進(jìn)行預(yù)處理,例如網(wǎng)絡(luò)請(qǐng)求HttpTransactionProducer生產(chǎn)者,對(duì)數(shù)據(jù)處理完畢后,發(fā)送廣播通知HttpTransactionConsumer消費(fèi)者接收處理數(shù)據(jù)。消費(fèi)者也使用統(tǒng)一的消費(fèi)者接口接收數(shù)據(jù),在消費(fèi)者中新建Http請(qǐng)求事務(wù)對(duì)象,設(shè)置接收數(shù)據(jù)、發(fā)送數(shù)據(jù)、請(qǐng)求方法、返回狀態(tài)等屬性后放入上傳數(shù)據(jù)對(duì)象。各種數(shù)據(jù)處理完畢后,上傳數(shù)據(jù)對(duì)象中包含設(shè)備信息、數(shù)據(jù)令牌、Http請(qǐng)求事務(wù)數(shù)組、Http錯(cuò)誤數(shù)組、Activity跟蹤隊(duì)列數(shù)組、Session屬性數(shù)組、分析事件數(shù)組等各項(xiàng)數(shù)據(jù)。
3.2.4 數(shù)據(jù)上傳
數(shù)據(jù)上傳模塊負(fù)責(zé)所有數(shù)據(jù)的檢查、序列化、上傳服務(wù)器,上傳模塊中有定時(shí)器周期性的檢查網(wǎng)絡(luò)狀態(tài)是否連接。如果網(wǎng)絡(luò)狀態(tài)已經(jīng)連接,從非阻塞隊(duì)列頭部取出數(shù)據(jù),直到隊(duì)列為空,最后生成一個(gè)統(tǒng)一的上傳對(duì)象。上傳模塊把對(duì)象中的所有屬性序列化為JSON格式,用Http Post方式上傳服務(wù)器。如果發(fā)送成功,清除上傳對(duì)象中的所有數(shù)據(jù),如果發(fā)送失敗,記錄發(fā)送嘗試次數(shù),等待下次再次發(fā)送。
3.3 Eclipse插件
Eclipse插件的作用是讓Eclipse啟動(dòng)時(shí)java虛擬機(jī)加載rewriter.jar包,并且給現(xiàn)有Andriod項(xiàng)目添加agent.jar包。
為了使java虛擬機(jī)啟動(dòng)時(shí)加載rewriter.jar包,需要在插件中實(shí)現(xiàn)IStartup接口的earlyStartup方法,在其中附著重寫類。這樣就可以在Eclispe執(zhí)行編譯前hook掉Android SDK和第三方庫(kù)中的類和方法。因?yàn)橐褂胻ools.jar包中的VirtualMachine類,所以java運(yùn)行環(huán)境一定要使用JDK,而不是JRE。earlyStartup方法中附著Instrumentation代理的代碼如下所示:
VirtualMachine vm = VirtualMachine.attach(pid);
vm.loadAgent(jarFilePath); //rewriter.jar的文件路徑
vm.detach();
其二,為了把a(bǔ)gent.jar包拷貝到項(xiàng)目的庫(kù)文件中,需要在插件中實(shí)現(xiàn)IObjectActionDelegate接口的run方法,開發(fā)人員在添加應(yīng)用性能采集功能到項(xiàng)目中時(shí),執(zhí)行run方法把a(bǔ)gent.jar拷貝到項(xiàng)目的libs目錄中。
該Android性能數(shù)據(jù)探針已經(jīng)在上海國(guó)際馬拉松Andriod項(xiàng)目中得到了實(shí)踐檢驗(yàn)。在應(yīng)用的首頁(yè)面嵌入啟動(dòng)探針代碼,后臺(tái)服務(wù)器就能獲得探針上傳的性能數(shù)據(jù),以Http請(qǐng)求為例,手機(jī)應(yīng)用的每次Http請(qǐng)求會(huì)在60秒內(nèi)以JSON格式上傳,上傳數(shù)據(jù)的JSON格式如下所示:
["http://m.shang-ma.com/shm-server/client","wifi",0.12600000202655792,200,0,0,2920,"POST"]
網(wǎng)址:http://m.shang-ma.com/shm-server/client
網(wǎng)絡(luò):wifi
總耗時(shí):0.12600000202655792秒
請(qǐng)求狀態(tài)碼:200
errorCode :0
發(fā)送字節(jié)數(shù) :0
接收字節(jié)數(shù):2920
Http方法:POST
嵌入探針后的安裝包和原有包相比體積增加對(duì)比如表3所示。
表3 加入探針前后安裝包大小對(duì)比
嵌入探針后的安裝包和原有包相比運(yùn)行時(shí)每天耗電量差值在5%以內(nèi),可用內(nèi)存差值可以忽略不計(jì)。
本文針對(duì)Android互聯(lián)網(wǎng)應(yīng)用需要采集各種性能數(shù)據(jù)和崩潰日志,現(xiàn)有埋樁方式使用不方便,容易漏掉數(shù)據(jù)和出錯(cuò)等問題,提出了使用面向AOP方式批量替換需要統(tǒng)計(jì)性能數(shù)據(jù)的類和方法。在編譯時(shí)自動(dòng)把探針功能加入,開發(fā)人員只需要添加簡(jiǎn)單的啟動(dòng)性能采集服務(wù)代碼,服務(wù)器就能獲得需要的性能數(shù)據(jù)和崩潰日志。
經(jīng)過實(shí)際使用證明,使用該方案開發(fā)出來的性能數(shù)據(jù)采集工具能夠減輕開發(fā)人員排查應(yīng)用錯(cuò)誤和慢交互的負(fù)擔(dān),而且不會(huì)影響原有應(yīng)用的功能和性能。下一步工作將增加對(duì)WebView交互性能監(jiān)測(cè)數(shù)據(jù)的采集和上報(bào)。
[1] 張廣紅, 陳平. 關(guān)于AOP實(shí)現(xiàn)機(jī)制和應(yīng)用的研究[J]. 計(jì)算機(jī)工程與設(shè)計(jì), 2003, 24(8):14-17.
[2] 寧紅巖, 廖小剛. 開發(fā)Agent的得力工具:Java[J]. 計(jì)算機(jī)工程與應(yīng)用, 2001, 37(11):119-121.
[3] éric Tanter, Toledo R, Pothier G, et al. Flexible metaprogramming and AOP in Java[J]. Science of Computer Programming, 2008, 72(1-2):22-30.
[4] Würthinger T, Wimmer C, Stadler L. Dynamic code evolution for Java[C]// International Conference on the Principles and Practice of Programming in Java. ACM, 2010:10-19.
[5] Yadav R, Bhadoria R S. Performance Analysis for Android Runtime Environment[C]// International Conference on Communication Systems and Network Technologies. 2015.
[6] Ravindranath L, Nath S, Padhye J, et al. Automatic and scalable fault detection for mobile applications[C]// International Conference on Mobile Systems, Applications, and Services. ACM, 2014:190-203.
[7] 喬一乘. 基于Android+JAVA EE架構(gòu)的校園信息交互系統(tǒng)[D]. 吉林大學(xué), 2012.
[8] 魏楚元, 李陶深, 張?jiān)龇? Eclipse:基于插件的下一代通用集成開發(fā)環(huán)境[J]. 計(jì)算機(jī)應(yīng)用與軟件, 2005, 22(6):38-40.
[9] Ciric A R, Floudas C A. A retrofit approach for heat exchanger networks[J]. Computers & Chemical Engineering, 2010, 13(6):703-715.
[10] 徐紹忠, 王乘, 劉小虎. Java并發(fā)機(jī)制研究[J].計(jì)算機(jī)工程, 2002,28(4):73-75.
[11] Ajay Kumar Jha, Woo Jin Lee. Capture and Replay Technique for Reproducing Crash in Android Applications[C]// The, Iasted International Conference on Software Engineering. 2013.
[12] 胡浩民.“單生產(chǎn)多重消費(fèi)”算法的提出與實(shí)現(xiàn)[J].計(jì)算機(jī)應(yīng)用與軟件, 2008,25(7):116-118.
RESEARCH ON PERFORMANCE DATA ACQUISITION PROBE OF ANDROID APPLICATION
Ma Sijun Xiao Rong Cheng Jiangwei
(ShanghaiIdealInformationIndustry(Group)Co.,Ltd,Shanghai201315,China)
Mobile applications have gradually become part of people’s daily life in social contact, e-commerce, tourism, health care, education, catering and trip. Due to the increasing fierce competition between application developers who want to improve user experience and increase the number of users, how to collect performance data and crash logs from the real mobile phone user is essential. In order to collect mobile application performance data has many defects by modifying the source code embedded pile, this paper proposes a method of pre-compiling the Android source code with aspect-oriented programming. It can automatically insert the performance data acquisition probe code without modifying the code manually. The method is used in many projects, and the Android application processed by this method can report the application performance and the data of user’s habits, which proves the feasibility of this method in practical application. The method for Android’s applied performance data and data collection provides a new way of thinking.
Android APM AOP Java agent ASM
2016-05-18。馬思峻,高工,主研領(lǐng)域:移動(dòng)應(yīng)用開發(fā),移動(dòng)設(shè)備管理,移動(dòng)性能監(jiān)控。肖榮,教授級(jí)高工。成江偉,工程師。
TP311.52
A
10.3969/j.issn.1000-386x.2017.07.036