前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android面试每日一题: Handler 如何有效地避免内存泄漏问题

Android面试每日一题: Handler 如何有效地避免内存泄漏问题

作者头像
老马的编程之旅
发布2022-09-21 09:55:44
6921
发布2022-09-21 09:55:44
举报
文章被收录于专栏:深入理解Android深入理解Android

在Android系统中,Handler是一个消息发送和处理机制的核心组件之一,与之配套的其他主要组件还有Looper和Message,MessageQueue。 Message和Runnable类是消息的载体。MessageQueue是消息等待的队列。Looper则负责从队列中取消息。

Handler有两个主要作用: 1.安排调度(scheule)消息和可执行的runnable,可以立即执行,也可以安排在某个将来的时间点执行。

2.让某一个行为(action)在其他线程中执行。

Handler是由系统所提供的一种异步消息处理的常用方式,一般情况下不会发生内存泄露。

Handler为什么可能造成内存泄漏。这里的内存泄漏,常常指的是泄漏了Activity等组件。

代码语言:javascript
复制
public class ShanActivity extends Activity{
    public Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
             
    }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
	}
}

这有什么问题呢。问题在于该Handler的实例采用了内部类的写法,它是ShanActivity这个实例的内部类,在Java中,关于内部类有一个特点:在java中,非静态的内部类和匿名内部类都会隐式的持有一个外部类的引用。所以,该handler实例持有了ShanActivity的一个引用。

生命周期较短的组件引用了生命周期较长的组件。Handler就是一种典型的示例,以上面的代码举例。ShanActivity可能会被泄漏,也就是该组件没有用了,比如调用了finish()后,垃圾回收器却迟迟没有回收该Activity。原因出在该实例的handler内部类引用了它,而该handler实例可能被MessageQueue引用着。

从上面的说法中,可以思考得到相应的解决方法:

1.保证Activity被finish()时该线程的消息队列没有这个Activity的handler内部类的引用。这个场景是及其常见的,因为handler经常被用来发延时消息。一个补救的办法就是在该类需要回收的时候,手动地把消息队列中的消息清空:mHandler.removeCallbacksAndMessages(null);

2.要么让这个handler不持有Activity等外部组件实例,让该Handler成为静态内部类。(静态内部类是不持有外部类的实例的,因而也就调用不了外部的实例方法了)

3.在2方法的基础上,为了能调用外部的实例方法,传递一个外部的弱引用进来)

4.将Handler放到抽取出来放入一个单独的顶层类文件中。

这里需要了解一下关于Java里面引用的知识:

在这里插入图片描述
在这里插入图片描述

第三种,需要一些额外的代码,比较通用。

代码语言:javascript
复制
public class ShanActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<ShanActivity> mActivity;
public MyHandler(ShanActivity activity) {
  mActivity = new WeakReference<ShanActivity>(activity);
}

@Override
public void handleMessage(Message msg) {
  ShanActivity activity = mActivity.get();
  if (activity != null) {
     //do Something
  }
}
}

第四种方式,抽取做单独封装。

代码语言:javascript
复制
/**
 * 实现回调弱引用的Handler
 * 防止由于内部持有导致的内存泄露
 * 传入的Callback不能使用匿名实现的变量,必须与使用这个Handle的对象的生命周期一 
 * 致否则会被立即释放掉了
 */
public class WeakRefHandler extends Handler {
    private WeakReference<Callback> mWeakReference;
    
    public WeakRefHandler(Callback callback) {
        mWeakReference = new WeakReference<Handler.Callback>(callback);
    }
    
    public WeakRefHandler(Callback callback, Looper looper) {
        super(looper);
        mWeakReference = new WeakReference<Handler.Callback>(callback);
    }
    
    @Override
    public void handleMessage(Message msg) {
        if (mWeakReference != null && mWeakReference.get() != null) {
            Callback callback = mWeakReference.get();
            callback.handleMessage(msg);
        }
    }
}

由于是弱引用,当该类需要被回收时,可以直接被回收掉。

WeakRefHandler的使用时如下:

代码语言:javascript
复制
    private Handler.Callback mCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch(msg.what){
            }
            return true;
        }
    };
    private Handler mHandler = new WeakRefHandler(mCallback);

tips:不使用这些方法,Handler直接持有Activity的引用是否一定会导致内存泄漏?

如果Handler中没什么耗时操作,任务完成也就释放了Activity引用。 如果Handler中是一个2秒的操作,在Activity退出的2s后,释放了Activity的指针,这种情况属于短时间的内存泄漏? Handler中是啥死循环的话,就内存泄露了。 个人写的Handler,且没有耗时操作。没必要保护。 公司项目对于代码规范的话,还是加上保护比较好。避免后续有同事,加上了耗时操作,出现问题。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档