首页 > Android笔记之Handler体系(二)
头像
惊蛰丶
发布于 2021-05-30 15:57
+ 关注

Android笔记之Handler体系(二)

先贴一下前一篇文章的传送门:

正文:
在上篇文章中,我们大体了解了Android的handler体系,弄明白了发送一个消息后,这个消息怎么就能来到handleMessage方法中从而被处理,但同时也留下了这样几个问题:一个线程中能不能有多个looper? 子线程中能不能直接使用handler?handler造成的内存泄漏该如何处理? 本篇文章我们便来一一进行说明

1.一个线程中能不能有多个looper?
    先说结论:不能
    那么为什么呢?要弄清楚这个问题,就必须要提到prepare()这个方法
   
public static void prepare() {
    prepare(true);
}


private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
这个方法的作用,简单来说就是为某个线程准备其对应的MessageQueue与Looper,可以看到,prepare()方法本身只是一个入口,真正逻辑都交给了prepare(boolean quitAllowed)这个方法来处理
而在这个方法中,首先会进行一个逻辑判断:如果sThreadLocal.get() 的值为空才进行设置,否则直接抛出一个异常,从异常信息我们也可以看出来,一个线程只能对应一个Looper
那么这个sThreadLocal是个什么玩意儿呢?简单来说就是一个ThreadLocal对象,用来进行线程间的数据隔离作用,让每个线程只能访问自身的数据,至于具体的原理和android的关系不大,这里便不做展开

2.子线程中能不能直接使用handler?
   结论:不能
   因为子线程是不会自带MessageQueue与Looper的,如果想要让子线程也拥有自己的handler,则必须先调用Looper.prepare()方法来创建Looper与MessageQueue
   如果直接在子线程中使用handler,则会抛出如下异常
  
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

可以看见,如果queue对象为空,则会直接抛出一个运行时异常,根据异常信息就可以发现,想要使用handler,就必须先拥有自身的Looper与MessageQueue


3.handler造成的内存泄漏该如何处理?
   如果直接在Activity中使用匿名类的形式来重写handleMessage方法可能会导致内存泄漏问题,这是因为在调用sendMessage方法后,会将Message的target属性设置为当前handler(具体流程可以参考我的上一篇文章),而当前handler又持有了当前Activity的引用,这就导致了Message间接持有了Activity的引用,而Message在被处理完成之后并不会立即被回收,而是会被存放到一个名为"消息池"(sPool)的地方 (我们获取消息时,调用的Message.obtain()方法,便是优先从消息池里去拿消息),这就可能让Activity实例无法被正常回收,从而导致内存泄漏
    要解决这个问题,我们一般是新建一个类去继承Handler类,然后在这个类里面利用弱引用获取Activity对象的实例,再进行业务操作,参考代码如下:
    
public class SaveHandler extends Handler {
    private WeakReference<MainActivity> myActivity;


     public SaveHandler(MainActivity activity){
        myActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg){
         MainActivity mainActivity = myActivity.get();
         /*
         * 执行业务逻辑
         *
         * */
    }
}

这样便可解决内存泄漏的问题,具体原理参考《深入理解Java虚拟机》一书



全部评论

(0) 回帖
加载中...
话题 回帖

推荐话题

相关热帖

近期热帖

近期精华帖

热门推荐