从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对象,所以第二次打印的结果就不一样了。
感谢一下文章: