Android技术知识Android开发经验谈Android开发

Android Framework | Handler 消息机制

2022-09-16  本文已影响0人  程序老秃子

引言

消息机制Android 中重要的线程间通信手段;它的存在可以让一个线程通知另一个线程去工作

众所周知 Handler 在 Android 中的地位非常重要, 从处理异步线程任务队列的 HandlerThread 到从子线程与UI线程的通信, 再到 ActivityThread四大组件sendMessage 调度, 再到进程间通信与之关联 Messenger 可以说是从头到尾贯穿的整个 Android 系统枝枝蔓蔓;所以说搞明白,搞懂 Handler 消息运行机制与原理至关重要

下面我们来看一下 Handler 的工作流程图

我们先从 Handler 源码看起吧,下一步看 MessageQueue 源码,最后看 Looper 源码

Handler 工作原理

Handler的主要工作是发送消息和接受消息

消息发送过程

消息的发送我们可以通过一系列 sendpost 方法
来看一下这些 sendpost 方法源码吧

    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

    public boolean sendMessageAtTime(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);
    }

    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }

    public final boolean postAtFrontOfQueue(Runnable r)
    {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

Handler 的send和post方法是很多,但是最终都调用了enqueueMessage方法。我们来看一下enqueueMessage方法源码

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

msg.target 就是当前的 Handler,queue.enqueueMessage(msg, uptimeMillis);就是将消息插入消息队列;发送消息的过程结束了,我们来看一下接收消息过程

消息接收过程

当有消息时 Looper 调用 MessageQueue 的 next() 方法取出消息,然后调用 msg.target.dispatchMessage(msg) 方法, msg.target 就是 Handler,上面已说过,这个方法就是 Handler 消息的处理,我们看一下 Handler 的 dispatchMessage 源码

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

Handler 构造方法

先看默认构造方法吧

 public Handler() {
     this(null, false);
}

会调用下面的构造方法

public Handler(Callback callback, boolean async) {
    ....省略
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

如果当前线程没有 Looper 就会抛出 Can't create handler inside thread that has not called Looper.prepare() 异常,这就是为什么在没有 Looper 的子线程中创建 Handler 会报这个异常原因了

Handler 还有一种特殊的构造函数,通过 Looper 来构造一个 Handler,构造函数源码如下

public Handler(Looper looper) {
    this(looper, null, false);
}

为什么要有Android消息机制?

我们知道 Handler 的主要作用是将一个任务切换到某个指定的线程去执行,比如 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呢?

这是因为 Android 的 UI 控件不是线程安全的,如果在多线程中访问 UI 控件则会导致不可预期的状态

那为什么不对 UI 控件访问加锁呢?

缺点有两个:首先加锁会让UI控件的访问的逻辑变的复杂;其次,锁机制会降低UI的访问效率

那我们不用线程来操作不就行了吗?

但这是不可能的,因为 Android 的主线程不能执行耗时操作,否则会出现 ANR;所以,从各方面来说,Android 消息机制是为了解决在子线程中无法访问 UI 的矛盾

上边分析了那么多, 看了那么一大堆的代码,我们清楚的了解到了 Handler 机制对于 Android 系统的重要性;所以也有人说 Handler 消息机制是 Framework 层的发动机,这么考虑一下一点也不为过吧;有需要了解更多关于Android Framework 消息机制 相关资讯的朋友;可以简信发送 "进阶" ,即可获取一份 Android Framework 思维导图及学习手册,以便大家能够更好的学习 Android Framework

Android Framework 思维导图

资料部分内容展示如下:

《Handler 机制之 Thread》

《Handler 机制之 ThreadLocal》

完整版 Android Framework 思维导图及学习手册 获取方式: 简信发送 "进阶" 即可 直达获取

对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们

技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面

Android 架构师之路还很漫长,与君共勉

上一篇下一篇

猜你喜欢

热点阅读