Handler 是一套 Android 消息传递机制,主要用于线程间通信。
为什要有Handler机制?
解决在子线程更新UI的问题
由于在Android机制中,为了保证UI操作是线程安全的,规定只允许在原始线程更新UI,但在实际开发中存在多个线程并发操作UI组件的情况,会导致线程不安全,所以采用Handler机制,当子线程需要操作UI组件时,通过Handler通知主线程,从而在主线程中更新UI。
Hnadler机制重要组成部分
Handler流程介绍
发送消息 --> 添加消息到队列 --> 从队列中获取消息 --> 处理消息
由Handler发送Messgae开始,将Message发送到MessageQueue中,由Looper不断的轮训去除MessageQueue中的Message,交给Handler处理 ,大致流程如下
发送消息的方式主要有sendXXX()
和postXXX()
两种,
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
handler.sendXXX()
和handler.postXXX()
最终都会调用到sendMessageAtTime()
方法。
public boolean sendMessageAtTime(@NonNull 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()
方法。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在这里将Hanlder本身赋值给Messgae
的target
,让后调用MessageQueue
的enqueueMessage()
,将Message
加入到MessageQueue
的队列当中,接下来就到MessageQueue的enqueueMessage()
方法当中去看。
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
......
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 当消息队列为空或者将要入队的消息(msg)的时间(when)在所有消息队列的消息最前面,则把msg插入到队头,最先执行
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 消息队列已经不为空了,再插入一个消息时,要先遍历所有的消息,根据时间先后排序,决定将新消息插入在什么位置
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
// 当p为空(遍历到最后一个了),或者新消息的时间在p的时间之前,就不用再遍历了,因为可以确定新消息要插在消息p前面了
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// break跳出for循环后执行到这里,将msg插入到p前面,prev后面
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
......
}
return true;
}
看Message
类的实现可以看出它是个单链表,消息队列中的消息都是按照时间先后顺序链接起来的。
接下来就详细的分析一下上述的代码,首先我们看一下这个if (p == null || when == 0 || when < p.when)
判断:
SystemClock.uptimeMillis()
加上延迟时间delayMillis
以上三个判断条件满足一个就会进入if代码块中,将新消息放在p消息的前面,当满足p==null时,即为队头,就是以下情况:
当满足p!=null
且when == 0 || when < p.when
为true
时,就是以下这种情况:
当以上三个条件不满足时,就会执行else的代码块,先来看一下下面这段代码的意思:
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
......
}
// break跳出for循环后执行到这里,将msg插入到p前面,prev后面
msg.next = p; // invariant: p == prev.next
prev.next = msg;
第一行代码中的P消息上在if代码块中添加的消息,说明指针指向P消息,第二行p消息指向下一个消息,指针小猴移动了一位。 从第二行代码可以看出,再次添加消息时,遍历消息都是从添加if
代码块中的消息开始的,因为从if
代码块中添加的消息要么就是第一个消息,要么就是when
小于p
消息的wnen
的,也就是说可确保前面的消息都是时间有序递增的。而系统的时间是一直在增加的,所以enqueueMessage()
方法的第二个参数when
一直大于mMessages
的when
,所以要把指针向后移一位,从后面一个消息开始比对。
以上两种都是在队头队尾添加的,再看一个从插入到队列中间的情况:
when
和p.when
的大小关系,小于p.when
,则把消息添加到p
的前面,否则循环消息队列找出合适的插入位置文章开始已经说了Looper
是消息轮询器,Looper.loop**
不断的从MessageQueue
中取出Message
,那么Looper.loop()
是在哪里调用的呢?由于Android是由消息驱动的,一定是在app的入口ActivityThread
的main()
方法中调用了,下面看下Looper.prepare()
的创建和Looper.loop()
轮训.
Looper.prepare()
对于app的入口ActivityThread
调用的是Looper.prepareMainLooper()
实际上也是调用了Looper.prepare()
。下面一步步来看一下:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到在Looper.prepareMainLooper()
中调用了Looper.prepare()
,在Looper.prepare()
中首先判断当前ThreadLocal
中是否已经存在Lopper
对象,如果存在则抛出异常,一个线程对应一个一个Looer
对象。如果不存在Looper
对象则 new一个出来,在Looper
的构造方法中创建了MessageQueue
。
ThreadLocal
保证了一个线程只有一个Looper
对象,一个Looper
对象只有一个MessageQueue
。看一下ThreadLocal
是如何做到的。回头看下sThreadLocal.get()
方法。
public T get() {//这里的泛型指的是Looper
Thread t = Thread.currentThread();
//获取当前线程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//获取当前线程中的Looper
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果map==null||looper==null执行以下方法
return setInitialValue();
}
private T setInitialValue() {
//初始化value(Looper)当然为空,在后面ThreadLocal.set()方法会赋上具体的值
T value = initialValue();
//获取当前线程ThradLocalMap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//添加进map
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
首先获取当前线程,根据当前线程获取到ThreadLocalMap
对象,ThreadLocalMap
是ThreadLocal
中的一个内部类,表明一个Thread
只存在一个ThreadLocalMap
,然后ThreadLocal.get()
方法中判断ThreadLocalMap
是否为空、ThreadLocalMap
中是否存在Looper
对象,如果存在则返回该Looper
对象,如果ThreadLocalMap
为空或者ThreadLocalMap
中不存在Looper
对象,则创建ThreadLocalMap
,赋值一个初始的空对象,等待ThreadLocal.set()
方法赋具体的值。
Looper.loop()
public static void loop() {
//获取到Looper对象
final Looper me = myLooper();
......
me.mInLoop = true;
//根据Looper对象获取MessageQueue
final MessageQueue queue = me.mQueue;
......
for (;;) {
// 从MessageQueue中取出消息,无消息时可能会阻塞
Message msg = queue.next();
......
try {
//交与Handler处理消息
msg.target.dispatchMessage(msg);
......
} catch (Exception exception) {
......
throw exception;
} finally {
......
}
......
//回收消息
msg.recycleUnchecked();
}
}
首先获取Looper
对象,根据Looper
对象获取到MessageQueue
队列,不断的调用next()
方法来获取Message
,交与Handler
去处理,最后回收该消息。
下面看一下MessageQueue.next()
做了些什么
// MessageQueue.java 中的 next 方法源码
Message next() {
// 判断 native 层的 MessageQueue 对象有没有正常创建
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 消息执行需要等待的时间
int nextPollTimeoutMillis = 0;
for (;;) {
// 执行 native 层的消息延迟等待,调 next 方法第一次不会进来
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 获取当前系统的时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
...
if (msg != null) {
if (now < msg.when) {
// 需要延迟, 计算延迟时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 不需要延迟获取已经过了时间,立马返回
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
// 标记为已在使用状态
msg.markInUse();
return msg;
}
} else {
// 如果队列里面没有消息,等待时间是 -1
nextPollTimeoutMillis = -1;
}
// 有没有空闲的 IdleHandler 需要执行,一般我们没关注这个功能
// 后面内容有专门解释,这里目前分析是 == 0 ,跳出
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
...
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
通过源码分析我们发现消息的处理过程,是通过当前消息的执行时间与当前系统时间做比较,如果小于等于当前系统时间则立即返回执行该消息,如果大于当前系统时间则调用 nativePollOnce
方法去延迟等待被唤醒,当消息队列里面为空时则设置等待的时间为 -1。
总结
Looper.loop()
之前必须调用Looper.prepare()
next()
方法处让CPU休眠,不会ANR处理消息
在Looper.loop()
中调用 msg.target.dispatchMessage(msg)
处理消息, msg.target
为Hanlder
,在enqueueMessage()
中赋值的。
下面看下dispatchMessage()
。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
msg.callback
是在Message.obtain()
方法中赋值的
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
如果msg.callback
不为空的话,就会执行handleCallback(msg)
方法,就是去处理这个Runnable
对象
private static void handleCallback(Message message) {
message.callback.run();
}
如果msg.callback
为空的话,就会判断Handler
中的回调是否为空,这个callback
对象是在Handler
创建时选择参数,如果不为空的情况下,就会执行callback
中的handleMessage(
)方法
public Handler(@Nullable Callback callback, boolean async) {
.......
mCallback = callback;
}
最后执行到我们常用的handleMessage(msg)
,这个方法就是一个空的模版方法,交由我们去实现具体的逻辑。
public void handleMessage(@NonNull Message msg) {
}
至此Handler
整个流程执行完毕。
Message创建
Message
创建的方法主要有两种
obtain()
从池子里去Message
对象public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
通过obtain()
从池子里去Message
对象,这里才用了享元设计模式,从池子里取出Message
对象复用,避免了不必要的对象创建,减少了内存开销,题高了资源的利用率.因此个人认为使用这种方法创建对象比较好。