美文网首页
Android Handler线程间通信原理分析

Android Handler线程间通信原理分析

作者: Gray_s | 来源:发表于2021-03-01 03:19 被阅读0次

    Android的Handler线程间通信作为面试必问,重要性不言而喻。作为开发者如何理解和利用进程间通信就变得尤为关键。本文将分三个部分剖析:使用方式、原理分析,如何利用。

    使用方式

    Handler的使用方式很简单

    • 处理消息
    //处理接受到的message
        var handler = object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
    
            }
        }
    //或
        var handler1 = Handler(Looper.getMainLooper(), object : Handler.Callback {
            override fun handleMessage(msg: Message): Boolean {
                return true
            }
    
        })
    

    代码中可以看到主要有两种方式,一种是重写handleMessage()方法,一种是实现Handler.Callback接口。这两种方式在优先级上有所不同,当实现Handler.callback接口时将优先执行接口的方法,当返回flase时才会执行重写的handleMessage()方法。两种方式中共有的参数,是表明使用的时哪个线程的Looper,最后就会在哪个线程执行方法。

    • 发送消息
            val obtainMessage = handler.obtainMessage()
            //  设置传入的数据
            obtainMessage.what = 1
            handler.sendMessage(obtainMessage)
    //或
            handler.post(object : Runnable {
                override fun run() {
                }
            })
    

    发送消息同样有两种方式,一种是获取message,传入数据然后发送,还有一种是直接post传入一个Runnable对象。就这两种方式而言,本质是一样的,第二种方式依然会转换成一个Message对象。但区别在于,如果handle重写了handleMessage()方法或者实现了Handler.Callback接口,那么将不会执行,只会执行postrun()方法。

    原理分析

    组成部分

    从使用的代码来看,线程间通信功能的实现至少有:

    • Handler
      用于处理消息的
    • Looper
      循环,用于分发消息
    • Message
      消息的载体

    但是只有以上的组件,并不能完成这个功能。从逻辑上来看:1、线程2的Looper分发线程1Messagehandler中去处理。2、在handler发送线程1的Message。在这两条逻辑上似乎缺少东西联系在一起。Looper从哪里获取到Message,handlerMessage发送到哪里去。这时就需要第4个组件:

    • MessageQueue
      存储Message,由Looper获取
      所以整个的通信流程如图所示
    线程间通信流程

    具体实现

    以主线程为例,在我们的App启动时最先执行的时启动Looper

    //android.app.ActivityThread#main
            Looper.prepareMainLooper();
            ...
            ActivityThread thread = new ActivityThread();
            ...
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
            ...
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
    

    上面就是启动Loop的主要方法,同时会创建ActivityThread对象,在这个对象中就有Handler对象。

    代码中的prepareMainLooper()是一个关键代码。

     public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    

    这段代码会先判断是否已有主线程的Loop,没有则会通过myLooper()创建所在线程的Looper,然年后再把Loop赋值给静态变量sMainLooper ,通过这个静态变量就可以跨线程获取主线程的Looper。而myLooper()只能获取当前线程的方法。原因就在于prepare(),和ThreadLocal对象。

        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");
            }
            //  把当前线程创建的Looper放入ThreadLocal中
            //  这个类提供线程局部变量。 这些变量与其正常的对应方式不同,因为访问一个的每个线程
            //(通过其get或set方法)都有自己独立初始化的变量副本
            sThreadLocal.set(new Looper(quitAllowed));
        }
    

    Handle中主要的方法为handleMessage()dispatchMessage()以及各种发送message方法。

        public void handleMessage(@NonNull Message msg) {
        }
        
        //表明 了消息处理的顺序
        public void dispatchMessage(@NonNull Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    MessageQueue中主要有5类方法

    • 创建MessageQueue
      nativeInit() Native方法
    • 添加Message进入队列
      enqueueMessage()
    • Message出队列
      next()
    • 删除队列中的Message
      removeMessage()
    • 销毁整个队列
      __nativeDestroy()__Native方法
      这些方法表面MessageQueue是一个容器用来存放Message。
      而Message对象,就是用来存放传递数据的容器。
      以上是4个组成部分主要的实现方式

    组件间关系

    知道了组件间的实现,那么组件间的是怎么调用的?
    依然从使用来看,首先handler要求传入Looper对象,handler会获取Message对象。所以我们会认为,handler持有Message、Looper对象。但当我MessageQueue对象却无法直接看到。从前面的分析中可以知道,MessageQueue是容器,Looper分发Message,所以Handler持有MessageQueue对象才更为合理,而Looper同样也应该持有MessageQueue对象,才能从其中获取、分发Message,Message同样应持有Handler对象,这个Looper才能知道该把Message发给哪里的Handler。
    在查看源码后确实如此(为了方便就不放出源码)


    关系图

    如何利用

    首先,线程间通信的主要功能就是跨线程。其次,在实现上来说,会利用到队列。针对这两个特点,我们可以加以利用。例如,高频发送的命令式异步任务,这个任务需,高频的发送命令,同时命令会有延时操作,而且新的命令会覆盖到已经进入队列但暂为执行的任务。这样的任务就适合直接利用Handler的结构进行解决,而不用自己维护一个队列。

    相关文章

      网友评论

          本文标题:Android Handler线程间通信原理分析

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