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虚拟机》一书



#Android##学习路径#
全部评论

相关推荐

当年还在美团那个倒霉的&nbsp;Peppr&nbsp;团队工作时,我一直有个疑问:这群人每天到底在自嗨什么。每次开会一堆人围着一堆“看起来很高级”的文档转,模板统一、名词复杂、页数感人,每一页都在暗示一件事:“你不懂,是因为你不专业。”但现实是——代码照样写在&nbsp;💩&nbsp;山上,该出问题还是会出问题,这真的很逗,系统一出问题,文档的唯一作用就是证明:“我们当初确实认真写过文档。”所以本质区别到底是什么?是代码质量提升了,还是大家在精神层面完成了一次“工程师&nbsp;cosplay”?有句话说得好潮水退去才知道谁在裸泳。还记得当时的马哥、明哥(图&nbsp;1&nbsp;左)最爱反复强调一句话:“所有场景一定要想到。”、“这个场景为什么没考虑到?”不过他们这些话我是真的听进去了。不然我也不会在一年多前就说:这个项目活不过两年。顺带一提,那段时间还有个固定节目。每次下楼,总能听见我明哥在吐槽不同的人。我从他身后绕过去,经常能听到他一边抽烟一边说:“xx&nbsp;这小子太坑了,回头我一定要跟马哥说说。”于是深谙人情世故但真不会抽烟的我也会从口袋掏出一支低尼古丁含量的烟给自己点上,假意自己什么都没听到什么都不知道,只是来抽烟的。后来我才明白,这可能也是团队文化的一部分:问题永远在别人身上,而我们,永远在复盘里😂。
秋招白月光
点赞 评论 收藏
分享
头像
2025-12-16 14:57
门头沟学院 Java
迷茫的大四🐶:是这样的,我都拿到你这同一水平的offer了,那我接你的offer的意义在哪,我一开始想接你们的offer期待是很高的,希望你们下次继续努力
你今年的保底offer是...
点赞 评论 收藏
分享
评论
1
7
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务