LeakCanary代碼量比較多兄朋,閱讀源碼容易把人繞暈,提取主干代碼,精簡后的代碼只有200行翔悠,看完這200行代碼业崖,LeakCanary檢測內(nèi)存泄露原理應(yīng)該就清楚了,回頭再看LeakCanary完整源碼就不再神秘。
精簡后的源代碼已發(fā)布在Github上:https://github.com/andev009/LearnLeakCanary
LeakCanary是安卓開發(fā)中分析內(nèi)存泄露的工具蓄愁。源代碼內(nèi)容很多双炕,總結(jié)起來主要分三個(gè)部分:1.檢測泄露,2撮抓,分析泄露妇斤,3,通知泄露丹拯。分析泄露用到了HAHA這個(gè)工具站超,有興趣的可以單獨(dú)分析,通知泄露乖酬,利用安卓自身消息機(jī)制死相,Service, Notification, Activity這些,按流程走就行了咬像。本文分析內(nèi)存泄露檢測這塊算撮,這塊才是LeakCanary原理的核心。
LeakCanary檢測內(nèi)存泄露分兩個(gè)部分:
1.監(jiān)聽Activity生命周期施掏,在Activity銷毀的時(shí)候通知LeakCanary钮惠。
2.內(nèi)存泄露檢測.
監(jiān)聽Activity生命周期
public class LearnLeakCanaryApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ActivityRefWatcher.install(this, new RefWatcher());
}
}
首先看入口,在Application完成注冊(cè)工作七芭,和LeakCanary源碼注冊(cè)不一樣素挽,用的ActivityRefWatcher完成注冊(cè),其實(shí)LeakCanary也差不多狸驳,不過多了各種配置预明。
注冊(cè)主要做兩件事,一是Application綁定Activity生命周期,Activity銷毀的時(shí)候都能監(jiān)聽到耙箍。二是new了個(gè)RefWatcher對(duì)象撰糠,RefWatcher就做了一件事,檢測泄露辩昆,如果泄露就捕捉給HAHA處理阅酪。這樣看,這一句代碼就完成了兩件重要的工作汁针。
ActivityRefWatcher注冊(cè)時(shí)最后的調(diào)用watchActivities术辐,在這個(gè)函數(shù)里完成Application和生命周期的監(jiān)聽注冊(cè)。
public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
lifecycleCallbacks就是生命周期回調(diào)施无,Activity銷毀時(shí)辉词,會(huì)被Application監(jiān)聽,然后走onActivityDestroyed回調(diào)猾骡。在onActivityDestroyed里瑞躺,把自己交給RefWatcher敷搪,讓RefWatcher去檢測自己是否真的被回收。
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override public void onActivityStarted(Activity activity) {
}
@Override public void onActivityResumed(Activity activity) {
}
@Override public void onActivityPaused(Activity activity) {
}
@Override public void onActivityStopped(Activity activity) {
}
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override public void onActivityDestroyed(Activity activity) {
Log.d(TAG, activity.getLocalClassName() + " onActivityDestroyed");
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
內(nèi)存泄露判斷
內(nèi)存泄露判斷主要是用到了WeakReference和ReferenceQueue幢哨,他們倆的關(guān)系很簡單赡勘,弱引用對(duì)象回收了,弱引用對(duì)象就會(huì)在ReferenceQueue里捞镰,如果ReferenceQueue里沒有狮含,就說明可能泄露。至于為什么會(huì)這樣曼振,看Reference源碼這里入隊(duì)列這段注釋几迄。
/**
* Adds this reference object to the queue with which it is registered,
* if any.
*
* <p> This method is invoked only by Java code; when the garbage collector
* enqueues references it does so directly, without invoking this method.
*
* @return <code>true</code> if this reference object was successfully
* enqueued; <code>false</code> if it was already enqueued or if
* it was not registered with a queue when it was created
*/
public boolean enqueue() {
return queue != null && queue.enqueue(this);
}
上面說可能泄露是因?yàn)镚C不一定及時(shí),所以LeakCanary會(huì)再調(diào)一次GC冰评,然后再檢測ReferenceQueue是否存在回收的對(duì)象映胁。如果這次還沒有就是泄露了,后面的邏輯就是給HAHA分析甲雅,Notification通知解孙。
void ensureGone(final KeyedWeakReference reference, String referenceName, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();
if (gone(reference)) {
return;
}
gcTrigger.runGc();
removeWeaklyReachableReferences();
if (!gone(reference)) {
//do HeapDump, HeapAnalyzer
Log.d("RefWatcher", Thread.currentThread().getName());
Log.d("RefWatcher",referenceName + " leaked!");
}
return;
}
整個(gè)流程很簡單,精簡后的代碼只有不到200行抛人。需要說明的是弛姜,LeakCanary在新線程里檢測,這里為了代碼簡單妖枚,用Handle postDelayed 5秒后在主線程檢測廷臼,原理都一樣。跑Demo時(shí)绝页,看ensureGone里泄露時(shí)的Log荠商。SecondActivity,ThirdActivity之所以內(nèi)存泄露是因?yàn)槔锩娴膬?nèi)部類持有外部引用续誉。仔細(xì)看看Log,應(yīng)該就理解原理了莱没。