【Android】小白进阶之WeakReference弱引用基础浅析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liwei16611/article/details/82415152

作为一枚 android 应用开发小白,工作中凡是遇到不懂的点都要做一番总结,希望对你有益。

1、弱引用定义

弱引用,与强引用相对,GC 在回收时会忽略掉弱引用对象(忽略掉这种引用关系)。

即,就算弱引用指向了某个对象,但只要该对象没有被强引用指向,该对象也会被GC检查时回收掉

2、示例

2.1、以 Handler 内存泄漏为例

Java使用有向图机制,通过 GC 自动检查内存中的对象,如果 GC 发现一个或一组对象为不可达的状态,则将该对象从内存中回收。也就是说:一个对象不被任何引用所指向,则该对象会在被GC发现的时候回收。

Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        mImageView.setImageBitmap(mBitmap);
    };
};

当使用内部类(或者匿名类)来创建 Handler 的时候,Handler 对象会隐式地持有一个外部类的对象(通常是Activity)的引用(否则怎么可能通过Handler来操作Activity的View?)。

而 Handler 通常会伴随着一个耗时的后台线程(比如:拉取网络图片),该后台线程在任务执行完毕后,通过消息机制通知 Handler,然后 Handler 把图片更新到界面上。

假设用户在网络请求过程中关闭了 Activity,正常情况下这个 Activity 不再被使用,就有可能被 GC 回收,但此时线程尚未执行完毕,而该线程持有 Handler 的引用(不然怎么发送消息给Handler?),Handler 又持有 Activity 的引用,就导致该 Activity无法被回收(内存泄漏),直到网络请求结束(如:图片下载完毕)。

另外如果执行了 Handler 的 postDelayed(),该方法会将 Handler 装入一个 Message,并把该 Message 推到 MessageQueue 中,由此产生了一条链式结构:MessageQueue->Message->Handler->Activity,导致 Activity 被持有引用而无法被回收。

总结:实例对象被其他对象持有引用,而无法被回收。

而弱引用便可以解决该问题:

被弱引用关联的对象只能生存到下一次 GC 发生之前。当GC工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。

public class AsyncDrawable extends BitmapDrawable {
    private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

    public AsyncDrawable(Resources res, Bitmap bitmap,
            BitmapWorkerTask bitmapWorkerTask) {
        super(res, bitmap);
        // bitmapWorkerTaskReference 实例关联 BitmapWorkerTask
        bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(
                bitmapWorkerTask);
    }
    public BitmapWorkerTask getBitmapWorkerTask() {
        return bitmapWorkerTaskReference.get();
    }
}

此时 WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference 关联了 bitmapWorkerTask 实例(可认为两者是“好朋友”关系),在虚拟机看来 bitmapWorkerTask 实例是垃圾时,但 WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference 不是垃圾。但垃圾并不会立即被回收,也就是仍然可以使用对象 bitmapWorkerTask。

若该对象已被清理,则必须重新构建对象,并再一次关联。

优点:

可以申请任意多个 bitmapWorkerTask 实例对象,并与弱引用对象关联,使用前先判断是否已被释放;如果已被释放,则重新申请,若未被释放,则直接使用。

使用了上述代码后,用户在关闭 Activity 之后,就算后台线程还没有结束,但由于仅有一个来自 Handler 的弱引用指向 Activity,所有 GC 仍然会在检查的时候把 Activity 回收掉。

2.2、详细示例

对于 handler 当一个 android 主线程被创建的时候,同时会有一个 Looper 对象被创建,而这个 Looper 对象会实现一个 MessageQueue (消息队列),当我们创建一个 handler 对象时,而 handler 的作用就是放入和取出消息从这个消息队列中,每当我们通过 handler 将一个 msg 放入消息队列时,这个 msg 就会持有一个 handler 对象的引用。因此当 Activity 被结束后,这个 msg 在被取出来之前,这 msg 会继续存活,但是这个 msg 持有 handler 的引用,而 handler 在 Activity 中创建,会持有 Activity 的引用,因而当 Activity结束后,Activity 对象并不能够被 gc 回收,因而出现内存泄漏。

根本原因:

Activity 在被结束之后,MessageQueue 并不会随之被结束,如果这个消息队列中存在 msg,则导致持有 handler 的引用,但是又由于 Activity 被结束了,msg 无法被处理,从而导致永久持有 handler 对象,handler 永久持有 Activity 对象,于是发生内存泄漏。

解决方式:

a、将 hanlder 对象声明为静态的对象

b、使用静态内部类,通过 WeakReference 实现对 Activity 的弱引用

static 类型可以解决这个问题,在java中所有非静态的对象都会持有当前类的强引用,而静态对象则只会持有当前类的弱引用。声明为静态后,handler 将会持有一个 Activity 的弱引用,而弱引用会很容易被 gc 回收,这样就能解决 Activity 结束后,gc 却无法回收的情况。

public class TestActivity extends Activity {

    public static class MyHandler extends Handler
    {
        protected WeakReference<Activity> m_oReference;

        public MyHandler(Activity activity)
        {
            // 弱引用 m_oReference 关联到 activity
            m_oReference = new WeakReference<Activity>(activity);
        }
    };

    private MyHandler mReceiveMsgFromThread = new MyHandler(this)
    {
        @Override
        public void handleMessage(Message msg)
        {
            try
            {
                super.handleMessage(msg);

                // 判断是否被释放
                if(null == m_oReference.get())
                {
                    return;
                }

                switch (msg.what)
                {
                  // null
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    };
}

refer:

https://www.cnblogs.com/CVstyle/p/6395745.html

https://www.cnblogs.com/l2rf/p/6047073.html

猜你喜欢

转载自blog.csdn.net/liwei16611/article/details/82415152