Android中解决由单例引起的内存泄漏问题

前言


Android 中 内存泄漏非常的常见,今天给大家讲下由“单例”引起的内测泄漏问题.


使用LeakCannary 检测内存泄漏



这个工具想必大家也很熟悉了,Jake大神公司出品,拒收,还可以检测出Android SDK 中存在的内存泄漏问题,可见,相当的厉害了,引入方式如下:


debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
    
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'



关于它的更多介绍,请参考github链接.




单例模式引起的内存泄漏


单例对象持有上下文Context的引发的内存泄漏


 因为单例对象的生命周期比较长,相当于应用程序Application的生命周期,如果Context使用的是当前Activity或者Fragment中的上下文,则生命周期较短,因此,当生命周期较长的对象引用了“较短”的上下文,会出现这样的情况,当Context所属的Activity或者Fragment 生命周期结束时(可能执行了onDestroy),Java的垃圾回收机制尝试回收该Activity时发现该类还存在引用(单例对象持有了这个引用),则会引发内存泄漏,因此推荐使用context.getApplicationContext()(即生命周期较长的上下文).

比如你可以这样写(伪代码)

ActivityManager getInstance(context.getApplicaionContext) .


ActivityManager 单例添加Activity实例对象引发的内存泄漏



 这个情况是我项目中的,比较特殊,因为我所负责的模块是以module依赖的形式存在于主项目中,因此不能使用Application这个类(大家应该知道原因吧),但是我还需要写一个对Activity进行管理的类,就有了ActivityManager这个单例类.

造成内存泄漏的原因和第一种Context的情况基本是一致的,但是处理方式不同,毕竟这个单例存储的是一个Activity的实例,泄漏原因可想而知,当Activity生命周期结束时,作为单例的ActivityManager还持有Activity的这个实例,因此引发了内存泄漏,这个时候,我好像没有办法使用全局的Context了,这个时候,我只能想到了谷歌推荐使用的弱引用WeakReference,也比较简单,修改好的单例写法如下:


package com.chaoxing.email.utils;

import android.app.Activity;
import android.content.Context;

import java.lang.ref.WeakReference;
import java.util.HashMap;

/**
 * Created by sdj on 2017/4/13.
 */

public class ActivityManager {
    private HashMap<String, WeakReference<Activity>> activities = new HashMap<>();
    private static ActivityManager activityManager;

    private ActivityManager() {
    }


    public static ActivityManager getInstance() {
        if (null == activityManager) {
            activityManager = new ActivityManager();
        }
        return activityManager;
    }

    public void addActivity(Activity activity) {
        if (null != activity) {
            activities.put(activity.getClass().getSimpleName(), new WeakReference<>(activity));
        }
    }

    public void exitApp() {

        for (String key : activities.keySet()) {
            WeakReference<Activity> weakReference = activities.get(key);
            if (null != weakReference && weakReference.get() != null) {
                weakReference.get().finish();
            }
        }
    }

    public static boolean isContextInvalid(final Context context) {
        return context == null || ((Activity) context).isFinishing();
    }
}


修改后运行了程序,关闭Activity,等待了10秒(LeakCannary的检测机制),没有发现泄漏,反复几次,并没有发现内存泄漏.


小彩蛋(算是丰富自己的博文吧,一些Tips吧 嘿嘿)


上面是项目中遇到的问题,对于大家平时开发过程中,经常会有泄漏的问题,而且在编码的过程中也是可以避免的,因为大谷歌开发的IDE神器 Android Studio(现在最新版是3.0 Beta4,我现在就是这个版本)有检测内存泄漏的提示.

比如下图注意看右边的黄色警告提示



  

然后IDE给出的提示(或者把鼠标直接放到该MailOptionHandler的类上)会出现以下的建议:

This Handler class should be static or leaks might occur (com.chaoxing.email.fragment.EmailPullFragment.MailOptionHandler) less... (Ctrl+F1) 
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

意思是该地方可能会存在泄漏,建议使用static 和 WeakReference 来进行修改.


按照该条建议,将该类修改为静态“static”,并使用了WeakReference 弱引用的持有该Fragment的对象实例,解决该泄漏.


Tips:

IDE的黄色建议,在开发不忙的情况下,是可以看的,优化自己的代码,一些判断语句的优化,IDE也会给出自己的建议的!


  





发布了60 篇原创文章 · 获赞 109 · 访问量 36万+

猜你喜欢

转载自blog.csdn.net/shenshibaoma/article/details/77980836
今日推荐