美文网首页
Handler的运行机制

Handler的运行机制

作者: 慎独静思 | 来源:发表于2022-03-14 22:58 被阅读0次

面试中经常会被问到handler的运行机制,如果你没认真研究过,可能只回答个大概。
比如,你会说这个过程涉及到的元素有:Handler,Message,MessageQueue,Looper,
Handler往MessageQueue发送Message,Looper不停的从MessageQueue取出Message进行处理。

这只是一个大概的过程,再细节的内容可能你也不了解了,今天我们就好好研究一下Handler,研究不透不放手的那种。
为什么我们要知道原理呢,会用不就行了吗?光会用还真不行,了解原理能帮助我们更好的使用,也能定位使用过程中遇到的问题,通过阅读源码,我们也可以学习源码的设计方式。

image.png

Handler往MessageQueue发送消息,消息入队列,Looper.prepare创建线程的looper,loop方法开启循环不断从MessageQueue中获取消息执行,最后回调给Handler处理。
Looper中用到了ThreadLocal来存储当前线程对象的Looper对象,ThreadLocal很神奇,注意用法。
Looper#loop方法会调用MessageQueue的next方法,因为next方法会阻塞,导致loop方法也会阻塞。
MessageQueue中通过单链表的数据结构来维护消息队列。它的next方法是一个无限循环的方法,如果消息队列中没有消息,则next方法一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将其从单链表中移除。

sendMessage.png

sendMessage实际是往MessageQueue插入一条消息,Handler#post方法其实也是调用sendMessage,只是要post的Runnable被作为Message的callback一起发送了,在处理消息时会先判断Message的callback是否为空,如下:

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

其中,dispatchMessage的逻辑如上,如果Message的callback不空,则执行callback,这个callback就是post的Runnable,如果Handler的callback不空,则执行callback的handleMessage,如果它返回false,则继续执行handler的handleMessage。

1、Activity a 启动一个Handler定时操作,启动Activity B,Activity B destroy时调用Handler#removeCallbacksAndMessages,Activity A中的handler操作是否继续?

答案是肯定的,继续操作。
Message中持有Handler,移除消息时判断了当前Handler和Message的Handler是否是同一个Handler。
也就是说对于主线程中new出来的多个Handler而言,他们都对应同一个Looper,同一个MessageQueue,但因为是不同的Handler对象,所以消息不会出现混乱。

2、Handler使用注意事项

private Handler mMainHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            // do something
        }
    };

通常我们在代码中使用Handler都是这样使用的,但是这样存在内存泄露的风险。
为什么会存在内存泄露的风险?
因为非静态的内部类或匿名内部类会持有外部类的引用,被延迟执行的Message中持有Handler的引用,Handler又持有Activity或Fragment的引用,导致Activity或Fragment不能正常释放,导致泄露。
解决方法:静态内部类+弱引用

private static class MyMainHandler extends Handler {
        
        private final WeakReference<Activity> mActivity;
        
        public MyMainHandler(Activity activity) {
            mActivity = new WeakReference<>(activity);
        }
        
        @Override
        public void handleMessage(@NonNull Message msg) {
            // do something
        }
    }

MyMainHandler handler = new MyMainHandler(this);

Activity或Fragment销毁时最好调用一下Handler#removeCallbacksAndMessages来移除队列中的消息。

3、主线程Looper创建过程

主线程的Looper是在ActivityThread的main方法中创建的。创建方法是调用Looper#prepareMainLooper方法,然后可以通过Looper#getMainLooper在任意地方获取Main Looper。

4、子线程中使用Handler的方式

new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler handler = new Handler();
                Looper.loop();
            }
        }).start();

在子线程中如果所有事情都完成以后应该调用Looper#quit方法终止消息循环,否则这个子线程就会一直处于等待状态,无法结束。
Looper的quit方法实际也是调用MessageQueue的quit方法,MessageQueue#quit方法被调用之后next方法会返回null,这样Looper#loop方法会结束。

5、Looper不停的运行,为什么主线程没卡死

Looper#loop方法会调用MessageQueue#next方法,next方法时阻塞的死循环操作,有消息时处理返回新消息,否则阻塞。这样保证主线程不会退出。并且ActivityThread会开启Binder线程执行其他操作,比如和AMS的交互,生命周期相关的事件最终都会发送到主线程的消息队列中进行处理。
参考:https://blog.csdn.net/ZytheMoon/article/details/105850489

6、为什么子线程不能更新UI

Android的UI相关操作均没有加锁处理,不能处理多线程并发操作。实际是一种单线程模型来保证线程安全。

void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

刷新UI时会在ViewRootImpl的requestLayout中检查当前线程。
参考:https://luisliu.cn/post/android/android-ui_thread/

7、什么情况下适合使用位运算

参考:https://mp.weixin.qq.com/s/67nMAJQt6FN1VsE_nayVoQ

相关文章

网友评论

      本文标题:Handler的运行机制

      本文链接:https://www.haomeiwen.com/subject/bfffdrtx.html