需求痛點(diǎn):leakCanary雖然很傻瓜了,但是必須要手動點(diǎn)開通知才能看到泄露信息袭厂,如果自動測試就無法保存現(xiàn)場仁堪。自動跑monkey和mtbf時(shí),出現(xiàn)了內(nèi)存泄露打却,但是沒有抓取到相關(guān)的信息杉适,無法分析,明知道有泄露但是無法找到原因柳击,非常痛苦猿推。
適用范圍:僅適用于java層的內(nèi)存泄露,基于leakCanary源碼實(shí)現(xiàn)捌肴。
編譯配置:
在gradle中編譯蹬叭,可參考源碼中的引用方式,在build.gradle中添加如下配置状知。
dependencies{
debugCompile project(':leakcanary-android')
releaseCompile project(':leakcanary-android-no-op');
}
在源碼中編譯秽五,Android.mk文件中配置如下。并將相應(yīng)jar包拷貝到對應(yīng)目錄下(此處為libs)饥悴,并保證名字相同坦喘。jar包獲取地址:auto-leak-detect.rar
LOCAL_AAPT_FLAGS := --auto-add-overlay
LOCAL_AAPT_FLAGS += --extra-packages com.squareup.leakcanary
LOCAL_STATIC_JAVA_LIBRARIES += dpt-haha-2.0.3.jar
LOCAL_STATIC_JAVA_AAR_LIBRARIES := \
dpt-leakcanary-watcher-1.4-beta2 \
dpt-leakcanary-analyzer-1.4-beta2 \
dpt-leakcanary-android-1.4-beta2 \
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
dpt-haha-2.0.3:libs/haha-2.0.3.jar \
dpt-leakcanary-analyzer-1.4-beta2:libs/leakcanary-analyzer-1.4-beta2.aar \
dpt-leakcanary-watcher-1.4-beta2:libs/leakcanary-watcher-1.4-beta2.aar \
dpt-leakcanary-android-1.4-beta2:libs/leakcanary-android-1.4-beta2.aar \
源碼編譯方式可參考郵件的提交:http://diana.devops.letv.com/#/c/257105/
如果需要加入混淆,請?jiān)诨煜渲梦募屑尤肴缦屡渲梦魃瑁@個(gè)配置在源碼的混淆文件中就有起宽,拷貝過來列在下面。
-dontwarn com.squareup.haha.guava.**
-dontwarn com.squareup.haha.perflib.**
-dontwarn com.squareup.haha.trove.**
-dontwarn com.squareup.leakcanary.**
-keep class com.squareup.haha.** { *; }
-keep class com.squareup.leakcanary.** { *; }
# Marshmallow removed Notification.setLatestEventInfo()
-dontwarn android.app.Notification
使用方式:
在自定義的Application中調(diào)用LeakCanary.install(this);
獲取自動保存的文件并分析:
在原來打開通知欄時(shí)济榨,我們會看到一個(gè)如下圖所示的列表坯沪,列表中每項(xiàng)都代表一個(gè)泄露,打開其中一項(xiàng)后擒滑,會有一個(gè)泄露的堆棧信息腐晾。


以源碼中的samle apk為例叉弦,產(chǎn)生的文件保存在/sdcard/Download/leakcanary-com.example.leakcanary中,此處com.example.leakcanary根據(jù)被檢查的宿主apk命名藻糖。打開文件夾淹冰,如下面左圖所示。
其中后綴名為.leakinfo的文件巨柒,就是我們自動加上的文件樱拴,打開其中一個(gè)查看,與上面的右圖中的堆棧信息一一對應(yīng)洋满【牵看看has leaked:后面的堆棧信息,如上面右圖中的堆棧信息是一樣的:root是一個(gè)Thread牺勾,references代表引用了一個(gè)對象正罢,這里是com.example.leakcanary.MainActivity$2.this$0,這個(gè)可能有多個(gè)驻民。最后leaks com.example.leakcanary.MainActivity instance代表導(dǎo)致了一個(gè)MainActivity的泄露翻具。當(dāng)然,文件中有比上圖中更豐富的信息,有興趣的同學(xué)可以再研究。


原理分析:
在源碼中弛姜,分析完內(nèi)存堆棧后,會直接生成一個(gè)Notification晾虑,同時(shí)還會保留一份dump出來的堆棧信息。但是仅叫,這個(gè)堆棧信息是不可讀的,我們只要把他轉(zhuǎn)換為可讀信息糙捺,然后保存就好了诫咱。
代碼:就是重寫DisplayLeakService.java文件中的afterDefaultHandling這個(gè)方法。完整的工程源碼請參考git地址:https://github.com/hhhqq/leakcanary/tree/master/leakcanary-android/src/main/java/com/squareup/leakcanary
/**
* You can override this method and do a blocking call to a server to upload the leak trace and
* the heap dump. Don't forget to check {@link AnalysisResult#leakFound} and {@link
* AnalysisResult#excludedLeak} first.
*/
protected voidafterDefaultHandling(HeapDump heapDump,AnalysisResult result,String leakInfo) {
booleanshouldSaveResult = result.leakFound|| result.failure!=null;
if(shouldSaveResult) {
saveResult2(heapDump,result,leakInfo);
}
}
private booleansaveResult2(HeapDump heapDump,AnalysisResult result,String leakInfo) {
File resultFile =newFile(heapDump.heapDumpFile.getParentFile(),
heapDump.heapDumpFile.getName() +".leakinfo");
FileOutputStream fos =null;
try{
fos =newFileOutputStream(resultFile);
fos.write(leakInfo.getBytes());
return true;
}catch(IOException e) {
CanaryLog.d(e,"Could not save leakInfo analysis result to disk.");
}finally{
if(fos !=null) {
try{
fos.close();
}catch(IOException ignored) {
}
}
}
return false;
}