從sample項目中的入口開始查看:
public class ExampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
enabledStrictMode();
LeakCanary.install(this);
}
private static void enabledStrictMode() {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() //
.detectAll() //
.penaltyLog() //
.penaltyDeath() //
.build());
}
}
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
public static boolean isInAnalyzerProcess(Context context) {
return isInServiceProcess(context, HeapAnalyzerService.class);
}
public static boolean isInServiceProcess(Context context, Class<? extends Service> serviceClass) {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
try {
packageInfo = packageManager.getPackageInfo(context.getPackageName(), GET_SERVICES);
} catch (Exception e) {
CanaryLog.d(e, "Could not get package info for %s", context.getPackageName());
return false;
}
String mainProcess = packageInfo.applicationInfo.processName;
ComponentName component = new ComponentName(context, serviceClass);
ServiceInfo serviceInfo;
try {
serviceInfo = packageManager.getServiceInfo(component, 0);//獲取serviceClass服務信息
} catch (PackageManager.NameNotFoundException ignored) {
// Service is disabled.
return false;
}
if (serviceInfo.processName.equals(mainProcess)) {
CanaryLog.d("Did not expect service %s to run in main process %s", serviceClass, mainProcess);
// Technically we are in the service process, but we're not in the service dedicated process.
return false;
}
int myPid = android.os.Process.myPid();
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.RunningAppProcessInfo myProcess = null;
List<ActivityManager.RunningAppProcessInfo> runningProcesses =
activityManager.getRunningAppProcesses();
if (runningProcesses != null) {
for (ActivityManager.RunningAppProcessInfo process : runningProcesses) {
if (process.pid == myPid) {
myProcess = process;
break;
}
}
}
if (myProcess == null) {
CanaryLog.d("Could not find running process for %d", myPid);
return false;
}
return myProcess.processName.equals(serviceInfo.processName);
}
前面是檢查當前app進程是否和內存分析服務的進程是否在同一進程,如果是,則不安裝內
存檢測。爲什麼是這樣子的呢?
查看內存分析的服務,發現前面有一段註釋
**
* This service runs in a separate process to avoid slowing down the app process or making it run
* out of memory.
*/
public final classHeapAnalyzerService
extends IntentService
意思是這個服務運行在單獨的進程避免使當前app運行慢或者內存溢出.由此可見內存分析服務分析過程中佔用資源多
接下來是如果判斷不在當前進程中,則進行安裝.代碼如下
public final classLeakCanary {
/**
* Creates a {@linkRefWatcher} that works
out of the box, and starts watching activity
* references (on ICS+).
*/
public staticRefWatcher
install(Application application) {
returnrefWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
首先創建一個AndoidRefWatchBuilder,這裏用到創建者模式.
/** Builder to create a customized {@linkRefWatcher}
with appropriate Android defaults. */
public static
AndroidRefWatcherBuilder
refWatcher(Context context) {
return newAndroidRefWatcherBuilder(context);
}
然後設置一個顯示分析內存分析後分析結果處理的監聽器.這個監聽器必須繼承抽象類AbstractAnalysisResultService
/**
* Sets a custom {@linkAbstractAnalysisResultService}
to listen to analysis results. This
* overrides any call to {@link#heapDumpListener(HeapDump.Listener)}.
*/
public AndroidRefWatcherBuilderlistenerServiceClass(
Class<? extendsAbstractAnalysisResultService> listenerServiceClass) {
returnheapDumpListener(newServiceHeapDumpListener(context,listenerServiceClass));
}
AbstractAnalysisResultService又繼承了IntentService
public abstract classAbstractAnalysisResultServiceextends
IntentService {
private static finalString
HEAP_DUMP_EXTRA=
"heap_dump_extra";
private static finalString
RESULT_EXTRA=
"result_extra";
ServiceHeapDumpListener代碼如下:
publicServiceHeapDumpListener(Context context,
Class<?extends
AbstractAnalysisResultService> listenerServiceClass) {
setEnabled(context,listenerServiceClass,
true);
setEnabled(context,HeapAnalyzerService.class,
true);
this.listenerServiceClass=
checkNotNull(listenerServiceClass,"listenerServiceClass");
this.context=
checkNotNull(context,"context").getApplicationContext();
}
裏面的setEnabled方法設置該服務可用(以前我們寫設置服務不可用,直接簡單粗暴的調用stopService方法,看大神寫的代碼才發現大神不愧是大神,考慮的那麼細緻).
public static voidsetEnabled(Context context,
finalClass<?> componentClass,
final boolean
enabled) {
finalContext appContext = context.getApplicationContext();
executeOnFileIoThread(newRunnable()
{
@Overridepublic void
run() {
setEnabledBlocking(appContext,componentClass,enabled);
}
});
}
public static void
setEnabledBlocking(Context appContext,Class<?>
componentClass,
boolean enabled) {
ComponentName component =new
ComponentName(appContext,componentClass);
PackageManager packageManager = appContext.getPackageManager();
int newState = enabled ?COMPONENT_ENABLED_STATE_ENABLED:
COMPONENT_ENABLED_STATE_DISABLED;
// Blocks on IPC.
packageManager.setComponentEnabledSetting(component,newState,DONT_KILL_APP);
}
繼續往下看
/**
* Creates a {@linkRefWatcher} that works
out of the box, and starts watching activity
* references (on ICS+).
*/
public static
RefWatcher install(Application application) {
returnrefWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
創建了一個ExcludedRefs.Builder的建造者。
/**
* This returns the references in the leak path that can be ignored for app developers. This
* doesn't mean there is no memory leak, to the contrary. However, some leaks are caused by bugs
* in AOSP or manufacturer forks of AOSP. In such cases, there is very little we can do as app
* developers except by resorting to serious hacks, so we remove the noise caused by those leaks.
*/
public static
ExcludedRefs.Builder
createAppDefaults() {
returncreateBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
}
添加了一個AndroidExcludedRefs.class 的
public staticExcludedRefs.BuildercreateBuilder(EnumSet<AndroidExcludedRefs>
refs) {
ExcludedRefs.Builder excluded = ExcludedRefs.builder();
for (AndroidExcludedRefs ref : refs) {
if(ref.applies)
{
ref.add(excluded);
((ExcludedRefs.BuilderWithParams) excluded).named(ref.name());
}
}
returnexcluded;
}
把所有要分析的AndroidExcludedRefs根據當前手機SDK版本和ROM作了過濾,註釋裏面也說得很清楚,隨着時間的推移,很多SDK 和廠商 ROM 中的內存泄露問題已經被儘快修復了
/**
* This class is a work in progress. You can help by reporting leak traces that seem to be caused
* by the Android SDK, here: https://github.com/square/leakcanary/issues/new
*
* We filter on SDK versions and Manufacturers because many of those leaks are specific to a given
* manufacturer implementation, they usually share their builds across multiple models, and the
* leaks eventually get fixed in newer versions.
*
* Most app developers should use {@link#createAppDefaults()}.
However, you can also pick the
* leaks you want to ignore by creating an {@linkEnumSet}
that matches your needs and calling
* {@link#createBuilder(EnumSet)}
*/
@SuppressWarnings({"unused","WeakerAccess"
}) // Public API.
public enum AndroidExcludedRefs
繼續往往下看..
/**
* Creates a {@linkRefWatcher} instance
and starts watching activity references (on ICS+).
*/
public RefWatcherbuildAndInstall()
{
RefWatcher refWatcher = build();
if (refWatcher !=DISABLED)
{
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.install((Application)context,refWatcher);
}
returnrefWatcher;
}
這個和前面的設置服務調用的是同一個方法
public static voidenableDisplayLeakActivity(Context
context) {
setEnabled(context,DisplayLeakActivity.class,
true);
}
public static voidinstall(Application application,RefWatcher
refWatcher) {
newActivityRefWatcher(application,refWatcher).watchActivities();
}
public voidwatchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
這裏註冊了一個activity什麼週期的回調
private finalApplication.ActivityLifecycleCallbackslifecycleCallbacks
=
newApplication.ActivityLifecycleCallbacks() {
@Overridepublic void
onActivityCreated(Activity activity,Bundle
savedInstanceState) {
}
@Overridepublic void
onActivityStarted(Activity activity) {
}
@Overridepublic void
onActivityResumed(Activity activity) {
}
@Overridepublic void
onActivityPaused(Activity activity) {
}
@Overridepublic void
onActivityStopped(Activity activity) {
}
@Overridepublic void
onActivitySaveInstanceState(Activity activity,Bundle
outState) {
}
@Overridepublic void
onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
這個回調有什麼用呢? 這個回調可就厲害了,app裏面所有activity什麼週期的都會在回調,可看到這裏的回調方法都對應的activity的生命週期方法。舉個栗子,在app裏面隨便在哪裏調用startActivity. Activity執行onCreate方法時會回調onActivityCreated()方法。以後寫退出app的輔助類是,就不用在BaseActivity裏面的onCreate()方法裏面添加當前activity,在onDestory裏面移除當前activity。當然這只是這個回調在其他地方的用處。扯遠了.
最最關鍵的代碼來了.將要銷燬的activity添加到觀察裏面
voidonActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
調用
/**
* Identical to {@link#watch(Object, String)}
with an empty string reference name.
*
* @see#watch(Object, String)
*/
public void
watch(Object watchedReference) {
watch(watchedReference,"");
}
最終調用。生成一個隨機的UUID當做key。把key添加到retainedKeys set集合裏面,把key和要檢測的對象封裝成KeyedWeakReference
/**
* Watches the provided references and checks if it can be GCed. This method is non blocking,
* the check is done on the {@linkWatchExecutor}
this {@linkRefWatcher} has been constructed
* with.
*
* @paramreferenceNameAn
logical identifier for the watched object.
*/
public void
watch(Object watchedReference,String
referenceName) {
if(this==
DISABLED) {
return;
}
checkNotNull(watchedReference,"watchedReference");
checkNotNull(referenceName,"referenceName");
//通過兩個差值計算某些代碼執行時間,比System.currentTimeMillis要精確
final long
watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
newKeyedWeakReference(watchedReference,key,referenceName,queue);
ensureGoneAsync(watchStartNanoTime,reference);
}
特定作了一個實驗
longstartcurrentTimeMillis=System.currentTimeMillis();//精確到毫秒
Thread.sleep(100);
System.out.println(System.currentTimeMillis()-startcurrentTimeMillis);
long startnanoTime=System.nanoTime();//精確到毫微秒
Thread.sleep(100);
System.out.println(System.nanoTime()-startnanoTime);
輸出結果
100
100126792
原來它就是可以精確到後面6位數.就和圓周率後面的小數點一樣,越多越精確。雖然說毫秒級的誤差應該是可以忽略不計的,但也讓我們知道了原來還有比毫秒更精確的一個方法的存在,說不定萬一哪天就遇到了呢。
繼續看代碼.
private voidensureGoneAsync(final
longwatchStartNanoTime, finalKeyedWeakReference reference) {
watchExecutor.execute(newRetryable()
{
@Overridepublic
Retryable.Resultrun() {
returnensureGone(reference,watchStartNanoTime);
}
});
}
最終檢測是否內存泄露的
@SuppressWarnings("ReferenceEquality")//
Explicitly checking for named null.
Retryable.Result
ensureGone(finalKeyedWeakReference
reference, final longwatchStartNanoTime) {
longgcStartNanoTime = System.nanoTime();
long watchDurationMs =NANOSECONDS.toMillis(gcStartNanoTime
- watchStartNanoTime);
//查看弱引用所引用的對象是否被回收了,如果被回收了則retainedKeys集合裏面移除當前弱引用的key
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached())
{
// The debugger can create false leaks.
returnRETRY;
}
//判斷弱引用的key是否還在,如果不在了,則說明該對象已經被回收了
if(gone(reference)) {
returnDONE;
}
//調用gc方法
gcTrigger.runGc();
//再判斷
removeWeaklyReachableReferences();
//如果對象依然存在,則說明該對象已經泄露了,然後獲取dumpHeap文件,分析dumHeap文件
if (!gone(reference)) {
longstartDumpHeap = System.nanoTime();
long gcDurationMs =NANOSECONDS.toMillis(startDumpHeap
- gcStartNanoTime);
File heapDumpFile =heapDumper.dumpHeap();
if (heapDumpFile ==RETRY_LATER)
{
// Could not dump the heap.
returnRETRY;
}
longheapDumpDurationMs =NANOSECONDS.toMillis(System.nanoTime()
- startDumpHeap);
//解析dumHeap文件
heapdumpListener.analyze(
newHeapDump(heapDumpFile,reference.key,reference.name,excludedRefs,watchDurationMs,
gcDurationMs,heapDumpDurationMs));
}
returnDONE;
}
private voidremoveWeaklyReachableReferences()
{
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
//如果弱引用所引用的對象沒有被垃圾回收器回收了,則queue.poll()返回null
while ((ref = (KeyedWeakReference)queue.poll())
!=null) {
retainedKeys.remove(ref.key);
}
}
//檢查當前引用的key是否在集合當中
private booleangone(KeyedWeakReference reference)
{
return!retainedKeys.contains(reference.key);
}
如果在還集合當中,運行gc
@Overridepublic void
runGc() {
// Code taken from AOSP FinalizationTest:
// https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
// java/lang/ref/FinalizationTester.java
// System.gc() does not garbage collect every time. Runtime.gc() is
// more likely to perfom a gc.
Runtime.getRuntime().gc();
enqueueReferences();
System.runFinalization();
}
最費解的當屬弱引用和其引用列隊的聯合使用,然後判斷當前弱引用所應用的對象是否被回收了。
這裏特例舉個栗子說明弱引用如何判斷所引用的對象是否被回收了
Person person=newPerson();
ReferenceQueue queue=newReferenceQueue();
final
KeyedWeakReference reference =
newKeyedWeakReference(person,"123","456",queue);
person=null;//顯示將person置爲null。
System.out.println("queue.poll():"+queue.poll());
// System.out.println("queue.get():"+reference.get());
System.gc();
try {
Thread.sleep(500);
}catch
(InterruptedException e) {
throw newAssertionError();
}
System.runFinalization();
System.out.println("queue.poll():"+queue.poll());
輸出結果
queue.poll():null
finalize.....
queue.poll():com.example.lib.KeyedWeakReference@75b84c92
當我們將person置爲null時,垃圾回收器並不會立即回收person對象,所以第一個poll的結果是null.當我們調用gc方法後,垃圾回收器發現person爲null了(因爲調用gc後垃圾回收器並不會立即執行,所以加延遲),於是就回收person對象,所以第二次打印的結果就不一樣了。
感謝一下文章: