准备☞Android 性能优化☞ 内存泄露工具 LeakCanary

背景介绍

之前一篇文章简单整理了Android 性能问题,这里的内存泄漏问题,是其中一部分,想来想去 还是单独整理这一部分,如果想了解Android 性能问题,请移步到 准备☞Android 性能优化

最近项目中一个内存泄露的问题,开始用MAT分析查看,结果太费劲了,还是使用简单粗暴的“武器”吧–LeakCanary (Square公司基于MAT开发的一款 开源的内存检测框架)

配置LeakCanary

1.配置build.gradle
最新的编译版本已经解决Android O (8.0)引入LeakCanary 产生异常问题“UnsupportedOerationException Could not find char array in java.lang.String”

dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.4'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
 }

2.权限配置

<application
        android:name=".DEVApplication">

 <!--SDCard中创建与删除文件权限-->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <!--向SDCard写入数据权限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    

3.在代码中动态添加权限

private static final int REQUEST_EXTERNAL_STORAGE = 1;
      private static String[] PERMISSIONS_STORAGE = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE" };

    public void checkPermissions(Activity activity) {
        try {
            //检测是否有写的权限
            int permission = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.WRITE_EXTERNAL_STORAGE");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限,去申请写的权限,会弹出对话框
                Toast.makeText(this, "请开通权限,否则无法使用", Toast.LENGTH_LONG).show();
                ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
            } else {
                Toast.makeText(this, "授权成功", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

在Activity 的 onCreate()方法里调用 checkPermission(this);

4.添加TestApplication 继承Application类,如果默认有Application类就不用添加XXXApplication了,直在默认的Application添加代码即可。

 public class TestApplication extends Application {
    private RefWatcher watcher
    @Override
    public void onCreate() {
        watcher = LeakCanary.install(this);
        super.onCreate();
    }
 }

//getRefWatcher静态方法,返回全局监控RefWatcher
public static RefWatcher getRefWatcher(Context context) {
        TestApplication testApplication = (TestApplication ) context.getApplicationContext();
        return testApplication.refWatcher;
    }

LeakCanary 默认监控所有Activity的内存泄露,我们也可以自定义监控对象。
在我们确定不再需要某个对象的时候,调用以下语句,就可以达到监视的目的。

watcher.watch(obj);

5.在MainActivity 添加监听泄露代码

@Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = DEVApplication.getRefWatcher(this);
        refWatcher.watch(this);
    }

Demo演示

下面我们来演示一个简单的内存泄露的问题,主要是由于 静态变量引起的 内存泄露问题。

DEVApplication.java


public class DEVApplication extends Application {
    private RefWatcher refWatcher;
    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = LeakCanary.install(this);
    }

    public static RefWatcher getRefWatcher(Context context) {
        DEVApplication devApplication = (DEVApplication) context.getApplicationContext();
        return devApplication.refWatcher;
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    static Context sContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sContext = this;
        checkPermissions(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = DEVApplication.getRefWatcher(this);
        refWatcher.watch(this);
    }

    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE" };

    public void checkPermissions(Activity activity) {
        try {
            //检测是否有写的权限
            int permission = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.WRITE_EXTERNAL_STORAGE");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限,去申请写的权限,会弹出对话框
                Toast.makeText(this, "请开通权限,否则无法使用", Toast.LENGTH_LONG).show();
                ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
            } else {
                Toast.makeText(this, "授权成功", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

发生内存泄漏后悔弹出Notification,如下图
这里写图片描述

点击后查看
这里写图片描述

可以看出产生内存泄露的原因:Activity由于被静态变量sContext引用,无法正常销毁。静态变量随着类的加载而存在,随着类的消失而消失,不会被GC回收。
解决方法:不要设置成 静态的变量


原理补充

Java对象的四种引用类

  • 强引用

    通过new出来的对象, 即使发生OOM也不会被GC回收。
    Object object = new Object();

  • 软引用

    在内存充足的时候和强引用一样,内存不足的时候会被GC回收。
    SoftReference softReference = new SoftReference(object);

  • 弱引用

    无论内存是否充足,触发GC的时候都会被回收。
    WeakReference weakReference = new WeakReference(object);

  • 虚引用

    任何时候都可能被回收。
    ReferenceQueue queue = new ReferenceQueue(); PhantomReference phantomReference = new
    PhantomReference<>(object, queue);
    ReferenceQueue – 引用队列
    和软引用,弱引用和虚引用配合使用,当引用的对象将要被JVM回收时,会将其加入到引用队列中。

  • ReferenceQueue – 引用队列
    和软引用,弱引用和虚引用配合使用,当引用的对象将要被JVM回收时,会将其加入到引用队列中。

  • Java垃圾回收机制 – 可达性算法

    整个Java对象的引用关系可以认为是一颗树,根节点是GCRoot。
    可达性算法指从GCRoot开始,能够遍历到的所有对象,认为是可达的,GC不会回收这些对象,没有被遍历到且存在于Java内存中的对象,就会被GC回收。

LeakCanary原理

利用弱引用被回收时会被加入引用队列的机制

  1. Install 方法在默认在Application注册一个Activity生命周期的回调方法,在Activity的onDestroy回调中进行watch监听。
  2. 创建一个监听对象的弱引用,放入set集合,手动触发GC。创建一个监听对象的弱引用,放入set集合,手动触发GC。
  3. 从引用队列中将被要被回收的对象取出来,然后将其从set里移除。从引用队列中将被要被回收的对象取出来,然后将其从set里移除。
  4. 如果移除后 set不为空,即说明set里的对象发生了内存泄露。如果移除后 set不为空,即说明set里的对象发生了内存泄露。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值