Android内存溢出与优化(四)——防止Handler导致的内存泄露

总篇地址:Android内存溢出与优化(零)——开题篇 



       在Android中,子线程不能直接更新主线程的UI,因此提供了Handler来方便我们操作。在子线程调用handler可以直接发送Message加入MessageQueue,Looper取出Message执行操作,这样就完成了从子线程到主线程的UI更新。但是,如果Looper取出的Message执行的操作是使用handler再发消息(发送的消息和之前一样),这样就形成了死循环,这样handler一直被持有引用,无法释放内存,导致内存泄露。


*****个人经验狭隘,难免有所疏漏,若有问题,恳请斧正!*****


1.什么时候会需要循环handler发消息?

       前面说handler形成死循环,会导致内存泄露。可是大家会想“谁会这么傻,没事弄个死循环?”。记得App里面常见的广告栏吗?会一直循环展示图片,这个时候就可能需要用到handler发送循环的延时消息。这里假设使用ViewPager循环展示图片,大致代码如下:

public class Demo extends AppCompatActivity {


    private static final int NEXT_PAGE = 1;

    private ViewPager mViewPager;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == NEXT_PAGE) {
                nextPage();//收到消息后,调用切换下一页的代码
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        mViewPager = (ViewPager) findViewById(R.id.viewpager);
        //其他操作……

        nextPage();
    }

    private void nextPage() {
        //切换至下一页
        int currentItem = mViewPager.getCurrentItem();
        mViewPager.setCurrentItem(++currentItem);
        mHandler.sendEmptyMessageDelayed(NEXT_PAGE, 4000);//4秒后切换下一页
    }
}

       可以看到,由于mHandler每次发送的延时4的秒消息,接收到消息后又继续调用了发送延时消息的方法nextPage(),这样就陷入了死循环。mHandler一直被调用,使Activity一直被持有引用,即便是Activity销毁了,也不会被释放掉内存,导致内存泄露。

2.如何解决handler循环导致的内存泄露?

       (一)使用WeakReference,使垃圾回收器倾向于回收该对象

        Java中对象有强、软、弱、虚四种引用,垃圾回收器优先回收顺序是虚>弱>软,不会回收持有强引用的对象(一直被持有强引用是导致内存泄露的原因)。平时建立的对象(例如Person person = new Person();),都是强引用,但是一旦使用完后,就不再持有引用,强引用消失,垃圾回收器就会来回收这片内存。而handler的循环使其引用一直不消失,所以不会被垃圾回收器回收,导致内存泄露。解决的代码如下:

private MyHandler mHandler = new MyHandler(DemoActivity.this);

    private static class MyHandler extends Handler {

        private final WeakReference<DemoActivity> mActivity;

        MyHandler(DemoActivity activity) {
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            DemoActivity activity = mActivity.get();
            if (activity != null) {

            }
        }
    }

         使用WeakReference后,该对象的引用变成了弱引用,垃圾回收器会优先回收没有引用的对象,当内存不足时就会回收这种不稳定的弱引用对象,释放出内存。

            (二)在Acticity销毁时,移除所有消息

         既然handler的所有Message、Callback都被移除了,自然就消除了循环导致的持续引用问题。解决的代码如下:

@Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);//移除MessageQueue内所有的Message、Callback
    }

        (三)在Application类中,定义一个全局的handler

         Application是一个全局的类,在App启动时启动,只有App关闭时才会销毁,所以完全不会存在内存泄露问题。设置代码如下:

/**
 * 自定义Application,进行全局初始化
 *
 * @author ALion on 2016/10/13 20:46
 */
public class MyApplication extends Application {

    private static Handler mHandler;


    @Override
    public void onCreate() {
        super.onCreate();

        mHandler = new Handler();
    }
    
    public static Handler getHandler() {
        return mHandler;
    }
    
}

——加油!




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值