一、Handler概述

二、Handler发送消息的方法

三、MessageQueue的enqueueMessage()

四、Message的when字段

五、子线程中使用Handler

六、Looper.loop()

七、获取下一个消息MessageQueue的next()

八、Handler的dispatchMesssage()

Handler在native层还有很多知识点,这篇主要围绕java层。

一、Handler概述

1. 什么是Handler?

Handler可以将一个任务切换到指定线程中执行,常用来实现在子线程工作完后切换到UI线程更新UI。

在ViewRootImpl中的checkThread()会检查当前线程,在更新UI时会调用到这个方法,所以在非UI线程中执行UI操作就会抛出此异常。

void checkThread() {

if (mThread != Thread.currentThread()) {

throw new CalledFromWrongThreadException(

"Only the original thread that created a view hierarchy can touch its views.");

}

}

UI控件不是线程安全的,多线程并发访问就会出现问题,所以只允许在主线程更新UI。之所以UI控件没有考虑上锁保证线程安全,是因为上锁会让代码逻辑变得复杂且会降低UI访问的效率。

2. Handler底层

Handler的运行需要底层的MessageQueue、Message和Looper来支撑。

a. MessageQueue

MessageQueue是消息队列。

采用单链表的形式存储消息列表。

向外提供对Message的插入和删除工作。

b. Message

消息实体。

c. Looper

Handler创建的时候采用当前线程的Looper来构造消息循环系统。

Looper以无限循环的形式查看是否有新消息。

有新消息时,就将Message对象从MessageQueue中取出,并将其交给Handler的dispatchMessage()。

Looper属于单个线程实例,通过ThreadLocal获得。

ThreadLocal可以在不同的线程中互不干扰地存储并提供数据,通过ThreadLocal可以很轻松地获取每个线程的Looper。

线程中默认是没有Looper的,如果需要,就要为线程创建Looper。

主线程在ActivtyThread创建时会初始化Looper,所以主线程默认可以使用Handler。

3. Handler工作步骤

Handler创建,采用当前线程的Looper构建内部消息系统。

Handler的post()、send()等方法调用MessageQueue的enqueueMessage()将一个Message加入到消息队列。

Looper发现新消息的到来,处理这个消息。

二、Handler发送消息的方法

发送消息的方法比较多,主要可以分为两类,一类sendXXXX(),一类postXxxx(),两类的主要区别就是send系的主要接受一个Message对象,而post系的主要接收一个Runnable对象,但最终也是封装成了Message对象。

boolean sendMessage(Message msg)

public final boolean sendMessage(Message msg)

{

return sendMessageDelayed(msg, 0);

}

boolean sendMessageDelayed(Message msg, long delayMillis)

public final boolean sendMessageDelayed(Message msg, long delayMillis)

{

if (delayMillis < 0) {

delayMillis = 0;

}

return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

}

boolean sendMessageAtTime(Message msg, long 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);

}

最后调用到MessageQueue的enqueueMessage()。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target = this;

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

}

上面三个send系的方法最终都是调用了MessageQueue的enqueueMessage(),出入了Message对象和执行时间。

boolean sendEmptyMessageDelayed(int what, long delayMillis)

sendEmptyMessage()系列的方法和上述方法类似,只是不需要传入一个Message,Message在方法中默认构造了。

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {

Message msg = Message.obtain();

msg.what = what;

return sendMessageDelayed(msg, delayMillis);

}

再来看post系的方法。

boolean post(Runnable r)

public final boolean post(Runnable r)

{

return sendMessageDelayed(getPostMessage(r), 0);

}

虽然传入的是Runnable对象,但是在方法调用过程中同样构造了Message,调用了send的方法,所以最终也是通过Message对象的形式交给MessageQueue的enqueueMessage()的。

private static Message getPostMessage(Runnable r) {

Message m = Message.obtain();

m.callback = r;

return m;

}

boolean postDelayed(Runnable r, long delayMillis)

postDelay()也同样是构造了Message对象,调用了sendMessageDelayed(),最终传递给MessageQueue的。

public final boolean postDelayed(Runnable r, long delayMillis) {

return sendMessageDelayed(getPostMessage(r), delayMillis);

}

三、MessageQueue的enqueueMessage()

在Handler的enqueueMessage()中,赋值了Message对象的target属性为执行的Handler对象,之后就调用了MessageQueue的enqueueMessage()。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target = this;

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

}

MessageQueue的enqueueMessage()还是比较长的。

第一步是一些异常处理,先判断msg的target是否为空、判断msg是否正在使用,这个是否正在使用是在下面同步代码块中进行赋值的。

进入同步代码块了,判断如果该msg已经调用了quit()边不再继续,直接回收返回。

进行了一些赋值操作后就开始加队列的工作了。

首先如果队列中还没有Message或该Message执行时间为0或是小于队列中的第一个Message的时间,就将该Message作为队列的头,并更新头Message记录。

否则遍历队列,将Message插入到合适的位置,可以看出整个队列是按Message的when(一个相对时间,表示执行时间)字段从小到大排列的,如果两个Mssage的when字段相同,则先入队列的排在前面。

最后,如果需要唤醒MesssageQueue的next()就唤醒(MessageQueue的next()在没有消息的时候会阻塞,而一旦添加了新消息,enqueueMessage()被调用,自然就唤醒了next())。

boolean enqueueMessage(Message msg, long when) {

// 1.

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) {

// 2.

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;

}

//3.

msg.markInUse();

msg.when = when;

Message p = mMessages;

boolean needWake;

// 4.

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 {

// 5.

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;

}

// 6.

if (needWake) {

// 此处唤醒next()。

nativeWake(mPtr);

}

}

return true;

}

四、Message的when字段

上面我们通过分析MessageQueue的enqueueMessage(),知道了Message队列是按照Message对象的when字段去排列的,那么when字段是什么呢?

在Handler的sendMessageDelayed中可以看到Message对象的when字段是如何初始化的。when的值是SystemClock.uptimeMillis() + delayMillis。

public final boolean sendMessageDelayed(Message msg, long delayMillis)

{

if (delayMillis < 0) {

delayMillis = 0;

}

return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

}

SystemClock#uptimeMillis()表示当前时间的一个相对时间,代表自系统启动开始从到调用该方法时度过的毫秒数。加上传参设置的delayMillis,整个when表示的时间代表该Message期望被分发的相对时间。

提供时间获取的方法有很多,为什么要用该系统启动到调用方法的相对时间,而不用System.currentTimeMillis()呢,是因为它代表的是从1970-01-01 00:00:00到当前时间的毫秒数,这个值是一个强关联系统时间,我们可以通过修改系统时间达到修改该值的目的,所以该值是不可靠的值,会有可能导致延时消息失效。when字段只是用时间差来表示先后关系,所以只需要一个相对时间就可以达成目的。

五、子线程中使用Handler

在UI线程中,Looper早已在app启动过程中就为我们初始化好了,所以可以直接获取Handler对象并使用,但是在子线程中,必须手动去初始化Looper,之后将Handler绑定该子线程的Looper才能够使用。

Looper.prepare(); // 为子线程创建一个Looper

mHandler = new Handler(){

@Override

public void handleMessage(Message msg) {

Log.d(TAG, "子线程的Handler");

}

};

mHandler.sendEmptyMessage(1);

Looper.loop();// 开始循环

可以向上面一样写,这里面有一些注意点。

在调用Handler之前必须调用Looper.prepare()方法,在当前线程中创建一个looper。我们也可以直接使用HandlerThread,其中是带有Looper的。

Looper.loop()是无限循环的,所以在Looper.loop()后边的程序代码块是无法执行到的。loop()方法的主要作用是一直不断的通过queue.next()方法来读取来自messagequeue中的msg,这个方法是block的状态,如果queue中没有消息的话会一直阻塞在这里。

除了上面的写法,也可以在Handler构造函数传入Looper对象来绑定。

Handler handler = new Handler(looper);

上面两种绑定Looper的方法中,第二种很直观通过传参绑定了,那么第一种方法是如何绑定的呢?

public Handler() {

this(null, false);

}

看到这里调用了Looper的myLooper(),猜想就是利用了ThreadLocal获取到当前线程的Looper对象了。

public Handler(Callback callback, boolean async) {

// ......

mLooper = Looper.myLooper();

// ......

}

果然是调用了TreadLocal对象的get,该ThreadLocal对象泛型是Looper的。

static final ThreadLocal sThreadLocal = new ThreadLocal();

public static @Nullable Looper myLooper() {

return sThreadLocal.get();

}

在Looper的prepare()中对该ThreadLocal放入了Looper对象,所以在使用Handler之前要先调用Looper的prepare()。

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

}

六、Looper.loop()

Looper.loop()

获取Looper对象并判空,再获取MessageQueue对象。

进入主循环,调用MessageQueue的next()去获取下一个消息,如果没有消息就一直阻塞。

如果返回消息为null,直接return,此时说明整个系统要处于退出状态了。

如果获取到了,就调用msg的Handler的dispatchMessage()执行消息。

之后将msg放入回收池中等待复用。

继续主循环,循环上述步骤,一直到退出。

public static void loop() {

// 获取looper对象

final Looper me = myLooper();

// 判空异常

if (me == null) {

throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

}

// 获取looper的MessageQueue

final MessageQueue queue = me.mQueue;

Binder.clearCallingIdentity();

final long ident = Binder.clearCallingIdentity();

// 开始死循环,不停获取消息队列中的消息并执行

for (;;) {

// 调用MessageQueue的next()获取新消息,这里可能被阻塞。

Message msg = queue.next(); // might block

// 如果返回了null,那么就说明MessageQueue已经quit了

if (msg == null) {

// No message indicates that the message queue is quitting.

return;

}

// ...... 日志输出等

try {

// 调用msg的Handler对象的dispatchMessage()去执行消息。

msg.target.dispatchMessage(msg);

end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();

} finally {

if (traceTag != 0) {

Trace.traceEnd(traceTag);

}

}

// ...... 日志输出等

// 将此msg放入回收对象池中,obtain()就是去复用这些对象。

msg.recycleUnchecked();

}

}

loop()不会自动退出,Looper#quit() 或者 Looper#quitSafely() 让它退出。两个方法都调用了MessageQueue#quit(boolean)方法,当MessageQueue#next()方法发现已经调用过 MessageQueue#quit(boolean)时会return null结束当前调用,否则的话即使MessageQueue 已经是空的了也会阻塞等待。

Q:既然是一个死循环,那为什么没有阻塞主线程呢?

如果说操作系统是由中断驱动的,那么Android的应用在宏观上可以说是 Handler机制驱动的,所以主线程中的 Looper不会一直阻塞的,原因如下:

当队列中只有延迟消息的时候,阻塞的时间等于头结点的 when 减去 当前时间,时间到了以后会自动唤醒。

在Android中 一个进程中不会只有一个线程,由于 Handler 的机制,导致我们如果要操作 View 等都要通过 Handler 将事件发送到主线程中去,所以会唤醒阻塞。

传感器的事件,如:触摸事件、键盘输入等。

绘制事件:我们知道要想显示流畅那么屏幕必须保持60fps的刷新率,那绘制事件在入队列时也会唤醒。

七、获取下一个消息MessageQueue的next()

MessgaeQueue # next()

这个方法可能是Handler机制中最长知识点最多的方法了,被上面的loop()调用,来获取下一个Message对象。

处理已经取消的情况。

进入循环调用nativePollOnce(),如果没有消息,就阻塞,如果有消息就继续。

获取第一个消息,如果第一个消息是barrier,就遍历去获取第一个异步消息。

enqueueMessage()会判断target不为空,barrier消息是在postSyncBarrier()中添加的,而异步消息是在Handler构造函数中指定的该Handlerf发送的消息是否为异步的。

如果消息时间到了就整理队列并返回该消息,如果每到就更新时间点。

如果此为第一遍循环,就去执行idleHandler。执行完之后复制变量让下一次循环不再执行此条。

Message next() {

// 处理Looper已经取消的情况,调用disposed()方法后mPtr=0

final long ptr = mPtr;

if (ptr == 0) {

return null;

}

// 记录空闲时需要处理的IdleHandler的数量。

int pendingIdleHandlerCount = -1; // -1 only during first iteration

// 表示距离处理下一个消息的时间,只要大于0就表明还有消息等待处理

int nextPollTimeoutMillis = 0;

for (;;) {

if (nextPollTimeoutMillis != 0) {

// 刷新Binder命令

Binder.flushPendingCommands();

}

// 调用native层,如果返回了就说明可以从队列中取出一条消息,如果消息队列中没有消息就阻塞等待

// 靠enqueueMessage()中最后一步调用nativeWake(mPtr)来唤醒该方法

nativePollOnce(ptr, nextPollTimeoutMillis);

// 上锁

synchronized (this) {

// 获取开机到现在的时间

final long now = SystemClock.uptimeMillis();

Message prevMsg = null;

// 表头第一个消息

Message msg = mMessages;

// 判断该Messagehi否是barrier

if (msg != null && msg.target == null) {

// 循环遍历出第一个异步消息,如果设置了barrier,就不能再执行同步消息了,除非将barrier移除。

// 但是异步消息不受影响照样执行,所以在这里要找到异步消息

do {

prevMsg = msg;

msg = msg.next;

// msg为null说明已经退出循环,为异步消息则消息为要找的

} while (msg != null && !msg.isAsynchronous());

}

if (msg != null) {

// 如果分发时间还没到

if (now < msg.when) {

// Next message is not ready. Set a timeout to wake up when it is ready.

// 更新执行时间点

nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);

} else {

// 如果时间到了

// Got a message.

mBlocked = false;

// 处理消息队列

if (prevMsg != null) {

prevMsg.next = msg.next;

} else {

mMessages = msg.next;

}

msg.next = null;

if (DEBUG) Log.v(TAG, "Returning message: " + msg);

// 标记msg正在使用

msg.markInUse();

return msg;

}

} else {

// 进此处是msg==null,再没有其它消息了

// No more messages.

nextPollTimeoutMillis = -1;

}

// Process the quit message now that all pending messages have been handled.

// 正在退出了,返回null。

if (mQuitting) {

dispose();

// 返回null,通知looper也停止

return null;

}

// 判断如果这是第一次循环(只有第一次循环时会小于0)并且队列为空或还没到处理第一个的时间

if (pendingIdleHandlerCount < 0

&& (mMessages == null || now < mMessages.when)) {

pendingIdleHandlerCount = mIdleHandlers.size();

}

if (pendingIdleHandlerCount <= 0) {

// No idle handlers to run. Loop and wait some more.

// 置为阻塞状态

mBlocked = true;

continue;

}

// 初始化最少四个要被执行的IdleHandler

if (mPendingIdleHandlers == null) {

mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];

}

mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);

}

// 开始循环执行所有的IdleHandler并根据返回值判断是否保留

for (int i = 0; i < pendingIdleHandlerCount; i++) {

final IdleHandler idler = mPendingIdleHandlers[i];

mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;

try {

keep = idler.queueIdle();

} catch (Throwable t) {

Log.wtf(TAG, "IdleHandler threw exception", t);

}

if (!keep) {

synchronized (this) {

mIdleHandlers.remove(idler);

}

}

}

// IdleHandler只会在消息队列阻塞之前执行一次,之后再不会执行,知道下一次被调用next()。

pendingIdleHandlerCount = 0;

// 当执行了IdleHandler后,会消耗一段时间,刺死可能已经到达执行消息的时间了,所以重置该变量再重新检查时间。

nextPollTimeoutMillis = 0;

}

}

八、Handler的dispatchMesssage()

在Looper的loop()中会调用该方法去处理msg,其实是一个分发过程。

msg.target.dispatchMessage(msg);

这里调用了target的方法,target字段是在Handler的enqueueMessage()中赋值的,为其Handler对象。这里调用了dispatchMessage()。

首先看msg中有没有callback对象,如果有,就交给callback执行。

如果没有再看有没有全局callback对象,如果有,就交给全局callback处理。

如果都没有或者全局callback处理不了,再调用handleMessage()。

public void dispatchMessage(Message msg) {

if (msg.callback != null) {

handleCallback(msg);

} else {

if (mCallback != null) {

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

}

这里的一系列优先级让我想起了View的事件分发。

首先判断的msg.callbck是调用post系方法时传入的runnable,这个执行的优先级最高。

private static void handleCallback(Message message) {

message.callback.run();

}

再就是判断mCallback,这个callback是在某些带有Callback参数的Handler构造函数中传入的。

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;

}

当然了,这个runnable完全可以返回false,设计在个人,如果想让这个msg还有机会交给Handler的handleMessage()去处理,就让它在合适的情况返回false。

最终到达handleMessage(),这个优先级最低了。

public void handleMessage(Message msg) {

}

我们最常用的就是在定义Handler对象时重写这个方法添加自己的逻辑。

android中handler机制,如何使用?,Android中的Handler机制相关推荐

  1. android token机制_对Android 中的 ANR 进行详解

    前言 关于ANR,以前只知道Activity.BroadCastReceiver.Service三种组件的ANR时限.一般采用哪些方式避免ANR.以及通过data/anr/traces.txt去分析A ...

  2. Android平台基于asmack实现XMPP协议中的PubSub机制

    Android平台基于asmack实现XMPP协议中的PubSub机制 本文主要介绍,在Android平台上基于asmack包,实现的PubSub机制,在PubSub中最重要的概念是节点Node,几乎 ...

  3. Android事件分发机制在实战开发中的应用之一

    学习的最终目标就是要学以致用,本文所分享的案例都是自己在公司实战开发过程中的真实案例,现在把它分享出来,希望对初学者有所帮助 版权声明:本文来自门心叼龙的博客,属于原创内容,转载请注明出处:https ...

  4. Android事件分发机制在实战开发中的应用之二

    学习的最终目标就是要学以致用,本文所分享的案例都是自己在公司实战开发过程中的真实案例,现在把它分享出来,希望对初学者有所帮助 版权声明:本文来自门心叼龙的博客,属于原创内容,转载请注明出处:https ...

  5. android handler的机制和原理_一文搞懂handler:彻底明白Android消息机制的原理及源码

    提起Android消息机制,想必都不陌生.其中包含三个部分:Handler,MessageQueue以及Looper,三者共同协作,完成消息机制的运行.本篇文章将由浅入深解析Android消息机制的运 ...

  6. 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 使用 DexClassLoader 获取组件类失败 | 失败原因分析 | 自定义类加载器没有加载组件类的权限 )

    文章目录 一.使用 DexClassLoader 获取组件类失败报错 二.失败原因分析 一.使用 DexClassLoader 获取组件类失败报错 在上一篇博客 [Android 逆向]启动 DEX ...

  7. android的消息处理机制(图文+源码分析)—Looper/Handler/Message[转]

    from:http://www.jb51.net/article/33514.htm 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.andro ...

  8. Android线程之异步消息处理机制(二)——Message、Handler、MessageQueue和Looper

    异步消息处理机制解析 Android中的异步消息处理主要有四个部分组成,Message.Handler.MessageQueue和Looper. 1.Message Message是在线程之间传递的消 ...

  9. 2023年中高级Android面试题汇总(不断更新中)

    前言 今年年底疫情宣布全面解封,由于疫情带来的影响2022年可谓是近十年以来最低谷的一天,但在2023年,我相信将会是面试求职的高峰时期,如果此时手里有份高质量的面试宝典,那么你将得心应手面对考官各种 ...

最新文章

  1. LeetCode简单题之杨辉三角 II
  2. 写给将要参加软考的朋友们
  3. VC代码的编写和调试---编写易于调试的VC代码
  4. 06.Spring 资源加载 - ResourceLoader
  5. select 1 from table
  6. WWW 2021 | 通过强化学习控制对话式检索的风险
  7. Redis持久化的几种方式——RDB深入解析
  8. 计算机网络应用基础论文,计算机网络应用基础概述论文
  9. Windows编程中的映射模式和坐标转换
  10. unity shader入门精要_Unity Shader 入门(一):渲染流水线
  11. vue中router-link绑定click失效
  12. .net mvc 获取url中controller和action
  13. CDLinux破解各种无线网络
  14. VCPKG 升级问题
  15. win10 1903 专业版 CreateProcessAsUser
  16. 1路编码器脉冲计数器或2路DI高速计数器,Modbus RTU模块 WJ150
  17. android 魔力锁屏源码,打造最炫手机锁屏桌面 10款安卓魔力锁屏主题推荐
  18. 50、ubuntu18.0420.04+CUDA11.1+cudnn11.3+TensorRT7.2/8.6+Deepsteam5.1+vulkan环境搭建和YOLO5部署
  19. 国内哪一家银行的账户最适合用来接收来自国外机构的美元汇款?
  20. 文字识别ORC与公式识别

热门文章

  1. 全球五大电信巨头宣布联手开发和融合4G技术
  2. 普元EOS应用,更改管理端口
  3. redies的单例安装
  4. 淫思奇巧篇 之 Save Actions 替你摆平代码格式问题
  5. 绝地武士Obi- Wan Kenobi
  6. SAP PS 第5节 标准WBS及标准网络
  7. c语言面试题sizeof,C语言面试题——sizeof的注意点
  8. 【R】【密度聚类、层次聚类、期望最大化聚类】
  9. CATIA二次开发——改变线型线宽颜色
  10. 区块链技术加持下的社交软件又能玩出什么新花样