检测App内存泄露--LeakCanary工具

luojia_lj 8年前

来自: http://blog.csdn.net/caroline_wendy/article/details/50580949

LeakCanary 是检测App内存泄露的工具, 内存泄露是Android开发中常见的问题, 使用程序的稳定性下降.

本文示例的Github 下载地址 .

主要原因:生命周期较长的类使用Activity的Context, 导致Activity被引用, 无法被及时回收. 解决方法, 除了需要页面支持类, 如Dialog等, 全部使用应用的Context, 即Context.getApplicationContext().

LeakCanary可以检查出页面的泄露问题, 并提供具体位置.

表明: LeakSingle的静态单例sInstance 引用 LeakSingle的mContext , 导致 MainActivity的instance 泄露.

我来讲解一下如何使用吧.

1. 依赖和引入

build.gradle的依赖.

    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' // or 1.4-beta1      releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1      testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1

引入应用

public class DemoApplication extends Application {      @Override public void onCreate() {          super.onCreate();          LeakCanary.install(this);      }  }

配置非常简单, 会增加一个附属应用, 去掉Application的引用, 就可以移除LeakCanary.

2. 泄露单例

泄露单例, 引入页面的TextView, 强制保留的父Activity, 会导致内存泄露, 可以在onDestroy时, 解引用避免.

/** * 泄露单例, 设计混乱, 单例只应该做事务性的工作, 页面操作应该使用回调. * 本示例仅做为反面示例, 切勿学习. * <p/> * Created by wangchenlong on 16/1/25. */  public class LeakSingle {      private Context mContext;      private TextView mTextView;        private static LeakSingle sInstance;        private LeakSingle(Context context) {          mContext = context;      }        public static LeakSingle getInstance(Context context) {          if (sInstance == null) {              sInstance = new LeakSingle(context);          }          return sInstance;      }        // 内存泄露      public void setRetainedTextView(TextView tv) {          mTextView = tv;          mTextView.setText(mContext.getString(R.string.app_name));      }        // 删除引用, 防止泄露      public void removeRetainedTextView() {          mTextView = null;      }  }

单例只应该做事务性的工作, 页面操作应该使用回调, 不是引入控件. 本示例仅做为反面示例, 切勿学习.

3. 泄露内存

调用单例, 两种引用都会导致内存泄露, 第一种是Context引用泄露, 第二种是子控件引用泄露. 避免方式是在onDestroy中, 清除引用.

最优方式:

在单例中只执行事务性工作, 不执行具体页面操作, 可以使用接口回调, 异步处理.

</div>
public class MainActivity extends AppCompatActivity {        @Bind(R.id.main_tv_text) TextView mTvText;        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          ButterKnife.bind(this);            /** * me.chunyu.spike.wcl_leakcanary_demo.MainActivity has leaked: * GC ROOT static me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.sInstance * references me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.mContext * leaks me.chunyu.spike.wcl_leakcanary_demo.MainActivity instance */  // LeakSingle.getInstance(this).setRetainedTextView(mTvText);            /** * me.chunyu.spike.wcl_leakcanary_demo.MainActivity has leaked: * GC ROOT static me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.sInstance * references me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.mTextView * references android.support.v7.widget.AppCompatTextView.mContext * leaks me.chunyu.spike.wcl_leakcanary_demo.MainActivity instance */          LeakSingle.getInstance(this.getApplication()).setRetainedTextView(mTvText);      }        @Override protected void onDestroy() {          super.onDestroy();          // 防止内泄露          LeakSingle.getInstance(this.getApplication()).removeRetainedTextView();      }  }

根据LeakCanary中的检测结果, 修改内存泄露的地方, 就可以完美的解决问题.

内存泄露的问题对于应用的用户体验至关重要, 感谢Square的产品, 让这件事变得如此简单.

OK, that’s all! Enjoy it!

</div>