编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 1.1App启动流程梳理
  • 1.2ActivityThread分析
  • 1.3Context设计思想
  • 2.1Activity基础介绍
  • 2.2Activity启动流程
  • 2.3Activity布局创建
  • 2.4Activity布局绘制
  • 2.5Service基础介绍
  • 2.6Service启动流程
  • 2.7Receiver广播基础
  • 2.8Receiver深入原理
  • 2.9ContentProvider分析
  • 2.10Fragment实践
  • 2.11Intent深入思考
  • 3.1Paint和Canvas
  • 3.2View的绘制基础
  • 3.3onMeasure流程设计
  • 3.4onLayout流程设计
  • 3.5onDraw流程设计
  • 3.6View工作原理
  • 3.7View刷新设计流程
  • 3.8自定义View控件
  • 3.9自定义ViewGroup控件
  • 4.1Handler基础使用
  • 4.2消息机制流程分析
  • 4.3Handler深度解析
  • 4.4Message深度理解
  • 4.5MessageQueue解析
  • 4.6Looper深度解析
  • 4.7理解Handler同步屏障
  • 4.8ThreadLocal分析
  • 4.9ThreadLocal场景
  • 5.1View事件设计思考
  • 5.2View滑动冲突处理
  • 5.3View事件源码分析
  • 5.4View事件总结案例
  • 6.1DecorView设计思想
  • 6.2视图的载体View
  • 6.3视图管理者Window
  • 6.4窗口管理服务WMS
  • 6.5布局解析者Inflater
  • 7.1AsyncTask深入介绍
  • 7.2HandlerThread设计
  • 7.3IntentService设计
  • 8.1IPC通信方式介绍
  • 8.2AIDL进程间通信
  • 8.7Binder通信机制设计
  • /zh/android/basic/9.1注解设计思想和原理.html
  • 9.2APT技术设计详解
  • 9.3APT多种案例实践

4.3Handler深度解析

目录介绍

  • 01.Handler构造方法
  • 02.sendMessage(msg)发消息
  • 03.postDelayed延迟消息
  • 04.handleMessage处理消息
  • 05.createAsync如何理解
  • 06.mainIfNull如何理解
  • 07.如何移除消息
  • 08.handler可以是多个吗
  • 09.发送消息的Delay可靠吗

01.Handler构造方法

首先看看构造方法,如果构造函数没有传入 Looper 参数,则会默认使用当前线程关联的 Looper 对象,mQueue 需要依赖于从 Looper 对象中获取。

如果 Looper 对象为 null ,则会直接抛出异常,且从异常信息 Can't create handler inside thread that has not called Looper.prepare() 中可以看到,在向 Handler 发送消息前,需要先调用 Looper.prepare()

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;
}

02.handler.sendMessage(msg)

看 handler.sendMessage(msg) 方法

关于下面得源码,是步步追踪,看enqueueMessage这个方法,原来msg.target就是Handler对象本身;

而这里的queue对象就是我们的Handler内部维护的Looper对象关联的MessageQueue对象。博客

handler.sendMessage(message);

//追踪到这一步
public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
//最终调用到 sendMessageAtTime 方法

可以看到 sendMessageAtTime()方法中需要一个已初始化的MessageQueue类型的全局变量mQueue,否则程序无法继续走下去。

而 mQueue 变量是在构造函数中进行初始化的,且 mQueue 是成员常量,这说明 Handler 与 MessageQueue 是一一对应的关系,不可更改

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);
}

然后接着往下看enqueueMessage方法

当中,需要注意 msg.target = this 这句代码,target 对象指向了发送消息的主体,即 Handler 对象本身。

即由 Handler 对象发给 MessageQueue 的消息最后还是要交由 Handler 对象本身来处理。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //target 对象指向的也是发送消息的主体,即 Handler 对象
    //即由 Handler 对象发给 MessageQueue 的消息最后还是要交由 Handler 对象本身来处理
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

看MessageQueue对象的enqueueMessage方法

看到这里MessageQueue并没有使用列表将所有的Message保存起来,而是使用Message.next保存下一个Message,从而按照时间将所有的Message排序。

因为存在多个线程往同一个 Loop 线程的 MessageQueue 中插入消息的可能,所以 enqueueMessage() 内部需要进行同步。

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

03.postDelayed延迟消息

使用 postDelayed 后消息队列会发生什么变化?这个消息是立即插入队列,还是延迟插入队列?

postDelayed的Message并不是先等待一定时间再放入到MessageQueue中,而是直接进入并阻塞当前线程,然后将其delay的时间和队头的进行比较,按照触发时间进行排序,如果触发时间更近则放入队头,保证队头的时间最小、队尾的时间最大。

此时,如果队头的Message正是被delay的,则将当前线程堵塞一段时间,直到等待足够时间再唤醒执行该Message,否则唤醒后直接执行。

问题,这个阻塞是如何实现的?

04.handleMessage处理消息

looper从消息队列里拿到消息后,然后交给handler去处理。看下 Handler 对象处理消息的方法

/**
 * 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);
    }
}

如果 msg.callback 不为 null ,则调用 callback 对象的 run() 方法,该 callback 实际上就是一个 Runnable 对象,对应的是 Handler 对象的 post() 方法

private static void handleCallback(Message message) {
    message.callback.run();
}

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

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

如果 mCallback 不为 null ,则通过该回调接口来处理消息,如果在初始化 Handler 对象时没有通过构造函数传入 Callback 回调接口,则交由 handleMessage(Message) 方法来处理消息,我们一般也是通过重写 Handler 的 handleMessage(Message) 方法来处理消息

05.createAsync如何理解

首先看一下源码

public static Handler createAsync(@NonNull Looper looper) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    return new Handler(looper, null, true);
}

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

然后看一下mAsynchronous变量是在哪里用到的?

相当于,如果把mAsynchronous设置成true,则会给Message对象赋值。那这个主要是做什么用的呢?异步消息,需要优先处理。后面在详细说这个

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

06.mainIfNull如何理解

@NonNull
public static Handler getMain() {
    if (MAIN_THREAD_HANDLER == null) {
        MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
    }
    return MAIN_THREAD_HANDLER;
}

/** @hide */
@NonNull
public static Handler mainIfNull(@Nullable Handler handler) {
    return handler == null ? getMain() : handler;
}

07.如何移除消息

Handler移除消息有多个api,如下所示:

handler.removeCallbacks(runnable);
handler.removeCallbacks(runnable,"token");
handler.removeCallbacksAndMessages(null);
handler.removeMessages(0);
handler.removeMessages(1,"");

08.handler可以是多个吗

09.发送消息的Delay可靠吗

不靠谱的,引起不靠谱的原因有如下

  1. 发送的消息太多,Looper负载越高,任务越容易积压,进而导致卡顿
  2. 消息队列有一些消息处理非常耗时,导致后面的消息延时处理
  3. 大于Handler Looper的周期时基本可靠(例如主线程>50ms)
  4. 对于时间精确度要求较高,不要用handler的delay作为即时的依据

如何优化保证可靠性

  1. 消息精简,从数量上处理
  2. 队列优化,重复消息过滤。这个如何做?
  3. 互斥消息取消
  4. 复用消息。其实Message已经用到了享元模式做到了消息创建复用!

消息空闲IdleHandler

MessageQueue.IdleHandler ideHandler =new MessageQueue.IdleHandler() {
        @Override
        public boolean queueIdle() {
            return false;
        }
    };
Looper.myQueue().addIdleHandler(ideHandler);

使用独享的Looper(HandlerThread)

HandlerThread handlerThread = new HandlerThread("A-Thread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
贡献者: yangchong211
上一篇
4.2消息机制流程分析
下一篇
4.4Message深度理解