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

欢迎Follow我的GitHub, 关注我的CSDN.

内存泄露

本文的合集已经编著成书,高级Android开发强化实战,欢迎各位读友的建议和指导。在京东即可购买:https://item.jd.com/12385680.html

Book

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

本文示例的Github下载地址.

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

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

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中, 清除引用.

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

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!

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SpikeKing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值