主流開源框架源碼深刻了解第4篇——Leakcanary源碼分析。(源碼以1.6.1版爲準)html
簡單說兩句,又有兩個多月沒寫文章啦,這中間雖然沒有繼續看源碼,不過卻是學了一些性能優化的知識,因爲基本都是經過視頻、博客等學習,並且本身的筆記也都是學習過程當中跟隨視頻和博客記下的,所以就沒有寫成文章發佈出來。感興趣的小夥伴能夠看一看:github.com/Endless5F/J…java
private static void initLeakCanary(Application sApplication) {
// LeakCanary 初始化(內存泄漏檢測)
if (LeakCanary.isInAnalyzerProcess(sApplication)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(sApplication);
}
複製代碼
ReferenceQueue:在適當的時候檢測到對象的可達性發生改變後,垃圾回收器就將已註冊的引用對象添加到此隊列中。後面代碼裏使用一個弱引用鏈接到你須要檢測的對象,而後使用ReferenceQueue來監測這個弱引用可達性的改變android
四大引用類型:git
GC線程掃描它所管轄的內存區域時,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些只具備弱引用的對象。github
除了強引用,其他3種引用均繼承自Reference類。Reference其內部提供2個構造函數,一個帶queue,一個不帶queue。其中queue的意義在於,咱們能夠在外部對這個queue進行監控。即若是有對象即將被回收,那麼相應的reference對象就會被放到這個queue裏。咱們拿到reference,就能夠再做一些事務。而若是不帶的話,就只有不斷地輪訓reference對象,經過判斷裏面的get是否返回null(phantomReference對象不能這樣做,其get始終返回null,所以它只有帶queue的構造函數)。這兩種方法均有相應的使用場景,取決於實際的應用。如WeakHashMap中就選擇去查詢queue的數據,來斷定是否有對象將被回收。緩存
Reference 把內存分爲 4 種狀態,Active 、 Pending 、 Enqueued 、 Inactive。性能優化
LeakCanary種就是WeakReference和ReferenceQueue聯合使用,若是弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。bash
ActivityRefWatcher:用於監控Activity,但只能用於Android 4.0及其之上 它經過watchActivities方法將全局的Activity生命週期回調接口 Application.ActivityLifecycleCallbacks註冊到application服務器
RefWatcher:做用 LeakCanary核心中的核心。RefWatcher的工做就是觸發GC,若是對象被回收,那麼WeakReference將被放入 ReferenceQueue中,不然就懷疑有泄漏(僅僅是懷疑),而後將內存dump出來,爲接下來的深刻分析作準備。app
ExcludedRef:LeakCanary提供了ExcludedRefs來靈活控制是否須要將一些對象排除在考慮以外,由於在Android Framework,手機廠商rom自身也存在一些內存泄漏,對於開發者來講這些泄漏是咱們無能爲力的,因此在AndroidExcludedRefs中定義了不少排除考慮的類
HeapDump.Listener與ServiceHeapDumpListener:ServiceHeapDumpListener實現了HeapDump.Listener接口。當RefWatcher發現可疑引用的以後,它將dump出來的Hprof文件經過 listener傳遞到HeapAnalyzerService。
HeapAnalyzerService:主要是經過HeapAnalyzer.checkForLeak分析對象的引用,計算出到GC root的最短強引用路徑。而後將分析結果傳遞給DisplayLeakService。
AbstractAnalysisResultService與DisplayLeakService:DisplayLeakService繼承了AbstractAnalysisResultService。它主要是用來處理分析結果,將結果寫入文件,而後在通知欄報警。
Heap Dump:Heap Dump也叫堆轉儲文件,是一個Java進程在某個時間點上的內存快照。
LeakCanary使用很是方便,不過實際上全部操做都是在源碼中完成的。
public static RefWatcher install(Application application) {
return refWatcher(application) // 獲取AndroidRefWatcherBuilder構造者
// 設置監聽服務的Class(注意:此處很重要)
.listenerServiceClass(DisplayLeakService.class)
// LeakCanary提供了ExcludedRefs來靈活控制是否須要將一些對象排除在考慮以外,
// 由於在Android Framework,手機廠商rom自身也存在一些內存泄漏,
// 對於開發者來講這些泄漏是咱們無能爲力的,因此在AndroidExcludedRefs中定義了不少排除考慮的類
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
// 構建並裝載
.buildAndInstall();
}
複製代碼
咱們簡單說一下系方法中的幾點:
// AndroidRefWatcherBuilder類:
// 是否觀察Activity的內存泄漏
private boolean watchActivities = true;
// 是否觀察Fragment的內存泄漏
private boolean watchFragments = true;
public RefWatcher buildAndInstall() {
// LeakCanaryInternals類是LeakCanary的一些相似於工具類的邏輯處理等(都是靜態方法)
if (LeakCanaryInternals.installedRefWatcher != null) {
// installedRefWatcher 用於保存是否構建並install過LeakCanary
// 若是重複構建並安裝LeakCanary,則會拋出以下異常
throw new UnsupportedOperationException("buildAndInstall() should only be called once" +".");
}
// 實例化RefWatcher對象,這個對象是用來處理泄漏對象的
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
// 默認爲true
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
// 默認爲true
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
複製代碼
從代碼中能夠看出buildAndInstall方法將build部分交給了build()方法,install部分使用ActivityRefWatcher和FragmentRefWatcher.Helper。
// RefWatcherBuilder類中,此類爲AndroidRefWatcherBuilder的父類
// 此構建方法中大部分都是獲取默認的值
public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}
// 此處不爲null
if (heapDumpBuilder.excludedRefs == null) {
heapDumpBuilder.excludedRefs(defaultExcludedRefs());
}
// 這裏的this.heapDumpListener 不爲null
HeapDump.Listener heapDumpListener = this.heapDumpListener;
if (heapDumpListener == null) {
heapDumpListener = defaultHeapDumpListener();
}
// 下面的默認爲null
// 用於查詢是否在 debug 調試模式下,調試中不會執行內存泄漏檢測。
DebuggerControl debuggerControl = this.debuggerControl;
if (debuggerControl == null) {
debuggerControl = defaultDebuggerControl();
}
// 用於產生內存泄漏分析用的 dump 文件。即 dump 內存 head。
HeapDumper heapDumper = this.heapDumper;
if (heapDumper == null) {
heapDumper = defaultHeapDumper();
}
// 執行內存泄漏檢測的 Executor
WatchExecutor watchExecutor = this.watchExecutor;
if (watchExecutor == null) {
watchExecutor = defaultWatchExecutor();
}
// GC 開關,調用系統GC。
GcTrigger gcTrigger = this.gcTrigger;
if (gcTrigger == null) {
gcTrigger = defaultGcTrigger();
}
if (heapDumpBuilder.reachabilityInspectorClasses == null) {
heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
}
// 初始化RefWatcher對象
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper,
heapDumpListener,
heapDumpBuilder);
}
複製代碼
咱們看一看build()方法幾個變量:
heapDumpBuilder.excludedRefs:此變量其實是在LeakCanary.install方法中經過excludedRefs設置的,用於過濾系統的一些內存泄漏。
this.heapDumpListener:此變量實際上同heapDumpBuilder.excludedRefs,是經過listenerServiceClass方法設置的。咱們簡單看一下其源碼:
// 1. AndroidRefWatcherBuilder類:
public AndroidRefWatcherBuilder listenerServiceClass(
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
// 2. ServiceHeapDumpListener類:
public ServiceHeapDumpListener(final Context context,
final Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
this.context = checkNotNull(context, "context").getApplicationContext();
}
// 3. RefWatcherBuilder類:
public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
// 此處就是1中heapDumpListener方法參數初始化的2對象
this.heapDumpListener = heapDumpListener;
return self();
}
複製代碼
所以heapDumpListener實際上就是ServiceHeapDumpListener對象。
其它:其它變量默認都是null,所以設置的都是默認值。
咱們已ActivityRefWatcher爲例說明:
// ActivityRefWatcher類:
// 經過提供的靜態方法初始化ActivityRefWatcher並註冊回調
public static void install(Context context, RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
// 初始化ActivityRefWatcher,並將build部分構建的RefWatcher傳入
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
// 註冊Activity的生命週期回調
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
// 生命週期回調
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override
public void onActivityDestroyed(Activity activity) {
// Activity進入Destroy狀態時開始監控其引用變化
refWatcher.watch(activity);
}
};
private final Application application;
private final RefWatcher refWatcher;
private ActivityRefWatcher(Application application, RefWatcher refWatcher) {
this.application = application;
// 初始化refWatcher成員
this.refWatcher = refWatcher;
}
複製代碼
咱們經過此部分代碼,能夠看出來,install靜態方法中初始化了ActivityRefWatcher的實例對象,而且註冊了Activity的生命週期的回調,最終在Activity的onDestroy狀態下開始使用refWatcher.watch監控當前Activity是否存在內存泄漏。
private final WatchExecutor watchExecutor;
private final DebuggerControl debuggerControl;
private final GcTrigger gcTrigger;
private final HeapDumper heapDumper;
private final HeapDump.Listener heapdumpListener;
private final HeapDump.Builder heapDumpBuilder;
private final Set<String> retainedKeys;
private final ReferenceQueue<Object> queue;
複製代碼
// RefWatcher類:
public void watch(Object watchedReference) {
// watchedReference爲Activity或者Fragment
watch(watchedReference, "");
}
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
// 返回的是JVM運行的納秒數
final long watchStartNanoTime = System.nanoTime();
// key值是用來最終定位泄漏對象用的,用來標識當前Activity或者Fragment的惟一值
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
// 建立一個弱引用,並傳入key和queue(引用隊列)
// KeyedWeakReference這個弱引用對象使用的是帶ReferenceQueue的構造方法
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
// 繼續執行
ensureGoneAsync(watchStartNanoTime, reference);
}
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
// 實際上使用的是Handler的post方法
// // 在異步線程上開始分析這個弱引用
watchExecutor.execute(new Retryable() {
@Override
public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
// 移除弱引用
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// 若是VM正鏈接到Debuger,忽略此次檢測,由於Debugger可能會持有一些在當前上下文中不可見的對象,致使誤判
return RETRY;
}
if (gone(reference)) {
// 若是引用已經不存在了則返回
return DONE;
}
gcTrigger.runGc(); // 觸發GC
removeWeaklyReachableReferences(); ;// 再次移除弱引用,二次確認
// 若是GC以後引用仍是存在,那麼就進行深刻分析
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
// dump出內存快照到*.hprof文件
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
// 構建HeapDump對象
HeapDump heapDump =
heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
// 對.hprof文件進行分析
heapdumpListener.analyze(heapDump);
}
return DONE;
}
複製代碼
這一大段代碼咱們分別來分析一下:
觸發watch動做過程分析:當Activity的onDestroy調用的時候,Application會收到通知,而後調用 lifecycleCallback.onActivityDestroyed()方法,最終RefWatcher的watch方法被觸發,也就實現 了Activity內存泄漏自動分析。
建立一個弱引用KeyedWeakReference,而當JVM觸發GC時,弱引用就會被回收,那麼此弱引用添加到引用隊列(ReferenceQueue)當中去。(WeakReference構造方法傳入ReferenceQueue隊列的時候,若引用的對象被回收,則將其加入該隊列。)
經過兩次移除弱引用,第二次以前手動調用gc操做。若還引用還存在,則生成堆內存dump文件,並初始化HeapDump對象,最後調用heapdumpListener.analyze分析並通知內存泄漏。
再來看兩個上段代碼中的兩個方法的具體源碼:
private boolean gone(KeyedWeakReference reference) {
// 判斷retainedKeys容器中有沒有key。
// 若回收了,就不存在key了,那麼就沒有泄漏,不然就懷疑有泄漏。
return !retainedKeys.contains(reference.key);
}
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
// 這個對象做爲弱引用,若回收了,那麼添加到引用隊列(ReferenceQueue)當中去,因此這個函數.poll是出棧的意思,
// 若是成功出棧了,那麼說明你加入了引用隊列,而後能夠認爲是已經被回收了
// 而後retainedKeys這個是一個Set容器,在以前會加入生成的惟一key做爲標識,這裏若是這個對象回收了,那麼就移除這個key值。
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
複製代碼
說明:此處的heapdumpListener就是上面【1). build部分】中的ServiceHeapDumpListener
此部分核心內容使用了另外一個庫:HaHa
// ServiceHeapDumpListener類:
@Override
public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
// HeapAnalyzerService類:
public static void runAnalysis(Context context, HeapDump heapDump
,Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
// Android 禁用與啓用 APP 或四大組件
// 請參考:https://blog.csdn.net/ShawnXiaFei/article/details/82020386
setEnabledBlocking(context, HeapAnalyzerService.class, true);
setEnabledBlocking(context, listenerServiceClass, true);
Intent intent = new Intent(context, HeapAnalyzerService.class);
// 此處listenerServiceClass,
// 即【LeakCanary.install(sApplication)】中提到的DisplayLeakService.class
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
// 開啓前臺服務
ContextCompat.startForegroundService(context, intent);
}
@Override
protected void onHandleIntentInForeground(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
// 獲取DisplayLeakService.class
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
// 初始化堆分析儀
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this,
heapDump.reachabilityInspectorClasses);
// checkForLeak就是最爲關鍵的方法
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile,
heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
// 將結果發送給偵聽器
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump,result);
}
@Override
public void onProgressUpdate(Step step) {
// 更新分析內存泄漏過程當中的進度狀態
int percent = (int) ((100f * step.ordinal()) / Step.values().length);
CanaryLog.d("Analysis in progress, working on: %s", step.name());
String lowercase = step.name().replace("_", " ").toLowerCase();
String message = lowercase.substring(0, 1).toUpperCase() + lowercase.substring(1);
// 開啓通知顯示
showForegroundNotification(100, percent, false, message);
}
複製代碼
analyze 方法開啓了HeapAnalyzerService堆分析服務,HeapAnalyzerService繼承自ForegroundService,而ForegroundService類繼承自IntentService。ForegroundService類重寫了onHandleIntent方法,並在該方法中調用了本身聲明的抽象方法onHandleIntentInForeground。而HeapAnalyzerService類又實現了AnalyzerProgressListener接口,該接口中只有一個方法onProgressUpdate(分析進度狀態更新)和分析過程當中的狀態枚舉。所以HeapAnalyzerService服務開啓後會直接執行onHandleIntentInForeground方法,最後執行到了一個最重要的方法checkForLeak:
// HeapAnalyzer類:
public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey,
boolean computeRetainedSize) {
long analysisStartNanoTime = System.nanoTime();
if (!heapDumpFile.exists()) {
Exception exception =
new IllegalArgumentException("File does not exist: " + heapDumpFile);
return failure(exception, since(analysisStartNanoTime));
}
try {
// 更新進度狀態
listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
// 利用HAHA(基於MAT的堆棧解析庫)將以前dump出來的內存文件解析成Snapshot對象
// 根據堆轉儲文件生成HprofBuffer緩存
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
// Hprof文件解析對象
HprofParser parser = new HprofParser(buffer);
listener.onProgressUpdate(PARSING_HEAP_DUMP);
// 解析過程,是基於google的perflib庫,根據hprof的格式進行解析
Snapshot snapshot = parser.parse();
listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
// 分析結果進行去重,可減小內存壓力
deduplicateGcRoots(snapshot);
listener.onProgressUpdate(FINDING_LEAKING_REF);
// 此方法就是根據咱們須要檢測的類的key,查詢解析結果中是否有咱們的對象,獲取解析結果中咱們檢測的對象
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
// 此對象不存在表示已經被gc清除了,不存在泄露所以返回無泄漏
if (leakingRef == null) {
return noLeak(since(analysisStartNanoTime));
}
// 此對象存在也不能確認它內存泄漏了,要檢測此對象的gc root
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
private Instance findLeakingReference(String key, Snapshot snapshot) {
// 由於須要監控的類,都構造了一個KeyedWeakReference
// 所以先找到KeyedWeakReference,就能夠找到咱們的對象
ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
if (refClass == null) {
throw new IllegalStateException(
"Could not find the " + KeyedWeakReference.class.getName() + " class in the " +
"heap dump.");
}
List<String> keysFound = new ArrayList<>();
// 循環全部KeyedWeakReference實例
for (Instance instance : refClass.getInstancesList()) {
List<ClassInstance.FieldValue> values = classInstanceValues(instance);
// 找到KeyedWeakReference裏面的key值,此值在咱們前面傳入的對象惟一標示
Object keyFieldValue = fieldValue(values, "key");
if (keyFieldValue == null) {
keysFound.add(null);
continue;
}
String keyCandidate = asString(keyFieldValue);
// 當key值相等時就表示是咱們的檢測對象
if (keyCandidate.equals(key)) {
return fieldValue(values, "referent");
}
keysFound.add(keyCandidate);
}
throw new IllegalStateException(
"Could not find weak reference with key " + key + " in " + keysFound);
}
private AnalysisResult findLeakTrace(long analysisStartNanoTime
, Snapshot snapshot,Instance leakingRef, boolean computeRetainedSize) {
listener.onProgressUpdate(FINDING_SHORTEST_PATH);
/**
* 這兩行代碼是判斷內存泄露的關鍵,咱們在上面中分析hprof文件,判斷內存泄漏
* 判斷的依據是展開調用到gc root,所謂gc root,就是不能被gc回收的對象,
* 查找泄露的最短引用鏈,gc root有不少類型,咱們只要關注兩種類型:
* 1.此對象是靜態 2.此對象被其餘線程使用,而且其餘線程正在運行,沒有結束
* pathFinder.findPath方法中也就是判斷這兩種狀況
*/
ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);
// 找不到引發內存泄漏的gc root,就表示此對象未泄漏
if (result.leakingNode == null) {
return noLeak(since(analysisStartNanoTime));
}
listener.onProgressUpdate(BUILDING_LEAK_TRACE);
// 生成泄漏的調用棧,爲了在通知欄中顯示
LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
String className = leakingRef.getClassObj().getClassName();
long retainedSize;
if (computeRetainedSize) {
listener.onProgressUpdate(COMPUTING_DOMINATORS);
// 反作用:計算保留的大小。
snapshot.computeDominators();
Instance leakingInstance = result.leakingNode.instance;
// 計算泄漏的空間大小
retainedSize = leakingInstance.getTotalRetainedSize();
// 檢查Android O以上,並查看android.graphics.Bitmap.mBuffer發生了什麼
if (SDK_INT <= N_MR1) {
listener.onProgressUpdate(COMPUTING_BITMAP_SIZE);
// 計算忽略的位圖保留大小
retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
}
} else {
retainedSize = AnalysisResult.RETAINED_HEAP_SKIPPED;
}
// 檢測到泄漏,構建AnalysisResult分析結果對象返回
return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
since(analysisStartNanoTime));
}
複製代碼
第三個分析步驟,解析hprof文件中,是先把這個文件封裝成snapshot,而後根據弱引用和前面定義的key值,肯定泄漏的對象,最後找到最短泄漏路徑,做爲結果反饋出來, 那麼若是在快照中找不到這個懷疑泄漏的對象,那麼就認爲這個對象其實並無泄漏。
最後,當內存泄漏分析完成,則調用AbstractAnalysisResultService.sendResultToListener:
// AbstractAnalysisResultService類:
public static void sendResultToListener(Context context
, String listenerServiceClassName,HeapDump heapDump, AnalysisResult result) {
Class<?> listenerServiceClass;
try {
listenerServiceClass = Class.forName(listenerServiceClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
// 開啓DisplayLeakService服務
Intent intent = new Intent(context, listenerServiceClass);
intent.putExtra(HEAP_DUMP_EXTRA, heapDump);
intent.putExtra(RESULT_EXTRA, result);
ContextCompat.startForegroundService(context, intent);
}
複製代碼
內存泄漏分析完成時,會開啓DisplayLeakService服務,該服務繼承自AbstractAnalysisResultService,而AbstractAnalysisResultService服務又繼承自ForegroundService,而AbstractAnalysisResultService重寫了ForegroundService服務的onHandleIntentInForeground(該方法是在onHandleIntent中調用),並在onHandleIntentInForeground方法中調用了onHeapAnalyzed方法,最終刪除了heapDumpFile文件。
// ForegroundService類:
@Override
protected void onHandleIntent(@Nullable Intent intent) {
onHandleIntentInForeground(intent);
}
// AbstractAnalysisResultService類:
@Override
protected final void onHandleIntentInForeground(Intent intent) {
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
try {
onHeapAnalyzed(heapDump, result);
} finally {
//noinspection ResultOfMethodCallIgnored
heapDump.heapDumpFile.delete();
}
}
// DisplayLeakService類:
@Override
protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
String leakInfo = leakInfo(this, heapDump, result, true);
CanaryLog.d("%s", leakInfo);
boolean resultSaved = false;
boolean shouldSaveResult = result.leakFound || result.failure != null;
if (shouldSaveResult) {
heapDump = renameHeapdump(heapDump);
// 保存內存快照和結果
resultSaved = saveResult(heapDump, result);
}
PendingIntent pendingIntent;
String contentTitle;
String contentText;
if (!shouldSaveResult) {
contentTitle = getString(R.string.leak_canary_no_leak_title);
contentText = getString(R.string.leak_canary_no_leak_text);
pendingIntent = null;
} else if (resultSaved) {
pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
if (result.failure == null) {
if (result.retainedHeapSize == AnalysisResult.RETAINED_HEAP_SKIPPED) {
String className = classSimpleName(result.className);
if (result.excludedLeak) {
contentTitle = getString(R.string.leak_canary_leak_excluded, className);
} else {
contentTitle = getString(R.string.leak_canary_class_has_leaked, className);
}
} else {
String size = formatShortFileSize(this, result.retainedHeapSize);
String className = classSimpleName(result.className);
if (result.excludedLeak) {
contentTitle = getString(R.string.leak_canary_leak_excluded_retaining, className, size);
} else {
contentTitle = getString(R.string.leak_canary_class_has_leaked_retaining, className, size);
}
}
} else {
contentTitle = getString(R.string.leak_canary_analysis_failed);
}
contentText = getString(R.string.leak_canary_notification_message);
} else {
contentTitle = getString(R.string.leak_canary_could_not_save_title);
contentText = getString(R.string.leak_canary_could_not_save_text);
pendingIntent = null;
}
// New notification id every second.
int notificationId = (int) (SystemClock.uptimeMillis() / 1000);
// 重點看這個方法
showNotification(this, contentTitle, contentText, pendingIntent, notificationId);
afterDefaultHandling(heapDump, result, leakInfo);
}
// LeakCanaryInternals類:
public static void showNotification(Context context
, CharSequence contentTitle, CharSequence contentText, PendingIntent pendingIntent, int notificationId) {
Notification.Builder builder = new Notification.Builder(context)
.setContentText(contentText)
.setContentTitle(contentTitle)
.setAutoCancel(true)
.setContentIntent(pendingIntent);
Notification notification = buildNotification(context, builder);
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notificationId, notification);
}
複製代碼
內存泄漏分析完成後,開啓DisplayLeakService服務,並調用onHeapAnalyzed方法,最終彈出通知告知 開發者內存泄漏的引用了,堆引用路徑。
對於Android開發來講,用leakcanary來檢測內存泄漏非常方便與快捷的。不過若LeakCanary沒法知足需求,能夠自定義將內存泄漏結果保存本地。
在LeakCanary中的DisplayLeakService.java類中有一個空方法,以下:
/**
* 您能夠重寫此方法,並對服務器進行阻塞調用以上傳泄漏跟蹤和堆轉儲。
* 不要忘記先檢查{@link AnalysisResult#leakFound and AnalysisResult#excludedLeak }
*/
protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
}
複製代碼
繼承DisplayLeakService類,重寫afterDefaultHandling()方法,實現本身的泄漏信息處理
public class LeadCanaryService extends DisplayLeakService {
@Override
protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
super.afterDefaultHandling(heapDump, result, leakInfo);
// 泄漏信息上傳雲端或者保存本地
saveLocal(result, leakInfo);
}
private void saveLocal(AnalysisResult result, String leakInfo) {
if (result != null) {
String leakPath = getApplication().getCacheDir().getAbsolutePath() + "/LeakCanary" +
"/LeakCanary.log";
File file = new File(leakPath);
FileUtils.createFileDir(file);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String leadMessage = "Time" + simpleDateFormat.toString() +
"\\n AnalysisResult{" +
"leakFound=" + result.leakFound +
", excludedLeak=" + result.excludedLeak +
", className='" + result.className + '\'' +
", leakTrace=" + result.leakTrace +
", failure=" + result.failure +
", retainedHeapSize=" + result.retainedHeapSize +
", analysisDurationMs=" + result.analysisDurationMs +
"} \\r\\n";
ByteArrayInputStream byteArrayInputStream =
new ByteArrayInputStream(leadMessage.getBytes());
try {
FileUtils.writeFile(byteArrayInputStream, file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
複製代碼
AndroidManifest.xml中註冊自定義服務LeadCanaryService
<service android:name=".service.LeadCanaryService"/>
複製代碼
Application中引用自定義服務LeadCanaryService
LeakCanary.refWatcher(this).listenerServiceClass(LeadCanaryService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
複製代碼
到此,LeakCanary分析就結束啦,對性能優化的小夥伴能夠看一下個人Github:github.com/Endless5F/J… ,有詳細的文檔和代碼參考。
注:如有什麼地方闡述有誤,敬請指正。期待您的點贊哦!!!