Android内存泄漏检测利器:LeakCanary

EdwardCople 8年前
   <h2>Android内存泄漏检测利器:LeakCanary</h2>    <h2>是什么?</h2>    <p>一言以蔽之:LeakCanary是一个 <strong>傻瓜化</strong> 并且可视化的内存泄露分析工具</p>    <h2>为什么需要LeakCanary?</h2>    <p>因为它简单,易于发现问题,人人可参与。</p>    <ul>     <li>简单:只需设置一段代码即可,打开应用运行一下就能够发现内存泄露。而MAT分析需要Heap Dump,获取文件,手动分析等多个步骤。</li>     <li>易于发现问题:在手机端即可查看问题即引用关系,而MAT则需要你分析,找到Path To GC Roots等关系。</li>     <li>人人可参与:开发人员,测试测试,产品经理基本上只要会用App就有可能发现问题。而传统的MAT方式,只有部分开发者才有精力和能力实施。</li>    </ul>    <h2>如何集成</h2>    <p>尽量在app下的build.gradle中加入以下依赖</p>    <p>在Application中加入类似如下的代码</p>    <pre>  <code class="language-java">public class ExampleApplication extends Application {      @Override public void onCreate() {         super.onCreate();          LeakCanary.install(this);     }  }</code></pre>    <p>到这里你就可以检测到Activity的内容泄露了。其实现原理是设置Application的ActivityLifecycleCallbacks方法监控所有Activity的生命周期回调。内部实现代码为</p>    <pre>  <code class="language-java">public final class ActivityRefWatcher {      private final ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacks() {          public void onActivityCreated(Activity activity, Bundle savedInstanceState) {          }            public void onActivityStarted(Activity activity) {          }            public void onActivityResumed(Activity activity) {          }            public void onActivityPaused(Activity activity) {          }            public void onActivityStopped(Activity activity) {          }            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {          }            public void onActivityDestroyed(Activity activity) {              ActivityRefWatcher.this.onActivityDestroyed(activity);          }      };      private final Application application;      private final RefWatcher refWatcher;        public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {          if(VERSION.SDK_INT >= 14) {              ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);              activityRefWatcher.watchActivities();          }      }  ....  }</code></pre>    <h2>想要检测更多?</h2>    <p>首先我们需要获得一个RefWatcher,用来后续监控可能发生泄漏的对象</p>    <pre>  <code class="language-java">public class MyApplication extends Application {   private static RefWatcher sRefWatcher;      @Override    public void onCreate() {              super.onCreate();              sRefWatcher = LeakCanary.install(this);      }        public static RefWatcher getRefWatcher() {              return sRefWatcher;      }  }</code></pre>    <p>监控某个可能存在内存泄露的对象</p>    <pre>  <code class="language-java">MyApplication.getRefWatcher().watch(sLeaky);</code></pre>    <h2>哪些需要进行监控</h2>    <p>默认情况下,是对Activity进行了检测。另一个需要监控的重要对象就是Fragment实例。因为它和Activity实例一样可能持有大量的视图以及视图需要的资源(比如Bitmap)即在Fragment onDestroy方法中加入如下实现</p>    <pre>  <code class="language-java">public class MainFragment extends Fragment {        @Override        public void onDestroy() {              super.onDestroy();              MyApplication.getRefWatcher().watch(this);        }  }</code></pre>    <h2>何时进行监控</h2>    <p>首先,我们需要明确什么是内存泄露,简而言之,某个对象在该释放的时候由于被其他对象持有没有被释放,因而造成了内存泄露。</p>    <p>因此,我们监控也需要设置 <strong>在对象(很快)被释放的时候</strong> ,如Activity和Fragment的onDestroy方法。</p>    <p>一个错误示例,比如监控一个Activity,放在onCreate就会大错特错了,那么你每次都会收到Activity的泄露通知。</p>    <h2>解决方案</h2>    <p>常用的解决方法思路如下</p>    <ul>     <li>尽量使用Application的Context而不是Activity的</li>     <li>使用弱引用或者软引用</li>     <li>手动设置null,解除引用关系</li>     <li>将内部类设置为static,不隐式持有外部的实例</li>     <li>注册与反注册成对出现,在对象合适的生命周期进行反注册操作。</li>     <li>如果没有修改的权限,比如系统或者第三方SDK,可以使用反射进行解决持有关系</li>    </ul>    <h2>如何实现的</h2>    <p>LeakCanary实际上就是在本机上自动做了Heap dump,然后对生成的hprof文件分析,进行结果展示。和手工进行MAT分析步骤基本一致。</p>    <h2>实践中的问题</h2>    <ul>     <li>如果targetSdkVersion为23,在6.0的机器上会存在问题,卡死,因为LeakCanary并没有很好支持 Marshmallow运行时权限 ,所以始终得不到sd卡权限,进而导致卡死。</li>    </ul>    <h2>注意</h2>    <ul>     <li>目前LeakCanary一次只能报一个泄漏问题,如果存在内存泄漏但不是你的模块,并不能说明这个模块没有问题。建议建议将非本模块的泄漏解决之后,再进行检测。</li>    </ul>    <p> </p>    <p>来自:http://www.jianshu.com/p/20484e01cf5b</p>    <p> </p>