Handler
在Android os
体系占举头轻重地位,关于一些用法,我想闭着眼睛各位都会写,那么关于源码是要分析的重点,Handler、Looper、MessageQueue、Message
与 同步消息 和 异步消息。
Handler
采用生产者-消费者模型,Handler
就是生产者,Looper
则是消费者具体实现给予dispatchMessage
,当然还少不了队列MessageQueue
,队列中的具体对象就是Message
发送消息
handler
发送消息有俩种sendMessagexxx()
和 postxxx()
,但是都会调用enqueueMessage
Handler.java
//post...
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
//....
//sendMessage...
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
//..
return enqueueMessage(queue, msg, uptimeMillis);
}
//重点❤ 队列、消息体、插入延迟时间毫秒
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
//默认变量 在handler() 产生 基础创建默认为 false
if (mAsynchronous) {
//重要❤ 重要的标识, 区分同步/异步
msg.setAsynchronous(true);
}
//重要❤,入队
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到最终都会调用enqueueMessage
MessageQueue queue
: 通过链表形式对Message
进行存储,并通过when
的大小对 Message
进行排序。
Message msg
:具体消息对象,long when
时间、Handler target
发送者、Message next
单链表指针
long uptimeMillis
: 插入延迟时间、默认0当前
重点❤mAsynchronous
:
标识区分当前发送的消息是属于什么类型,同步消息 True
或者 异步消息 False
下面在looper
具体分析....
消息插入
MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//保证多线程入队先加锁
synchronized (this) {
//....
msg.markInUse(); //标记正在使用
msg.when = when; // when 属性
Message p = mMessages; //拿到链表头部的消息
boolean needWake;
//重点❤ 满足以下3种情况之一就把msg插入到链表头部
//1.队列为null
//2.当前时间没有延迟0
//3.插入的时间比链表的对象时间早
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; //如果处于阻塞状态,需要唤醒
} else {
//重点❤ 唤醒标识 ,
//4.如果p != null且msg并不是最早触发的,就在链表中找一个位置把msg插进去
//5.如果处于阻塞状态,并且链表头部是一个同步屏障(target为null的Message),并且插入消息是最早的异步消息,需要唤醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
//当遍历到队尾、或者是 msg的时间比当前时间更早
if (p == null || when < p.when) {
break;
}
//发现了异步消息的存在,不需要唤醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//单链表、插入msg信息
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// 唤醒 Native 中的 MessageQueue线程
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
总结
enqueueMessage
有返回值,插入是否成功,true
表示插入成功,false
表示插入失败
同步屏障就是 msg.target=null 即 handler=null
的Message
-
插入链表的头部
-
Message
触发根据时间when
判断,越早触发越会向前排列,当生产者存入消息时,message
没有携带hander
或者是异步消息
时间最早就会优先排列
-
-
插入链表中部
- 如果插入的消息不是空,且插入的消息不是最早有一定的延迟,就会遍历
MessageQueue
队列找到合适的位置,保证队列的执行顺序有序
- 如果插入的消息不是空,且插入的消息不是最早有一定的延迟,就会遍历
-
判断是否调用
nativeWake
方法根据状态值是否为
true
,来决定是否调用nativeWake
方法唤醒当前的线程的MessageQueue
- 如果插入的消息在链表的头部,
needWake = true
表示此时nativePollOnce
方法进入阻塞状态,等待被唤醒返回 - 如果插入的消息在链表的中部,如果链表头是一个消息屏障,同时插入的是一个最早的异步消息,需要唤醒。
- 如果插入的消息在链表的头部,
消息轮动
1、Looper的创建
在Android中应用的程序入口是**ActivityThread.java -- main **方法,而应用消息的循环也是在这里创建,参考源码
public static void main(String[] args) {
//创建消息循环Looper
Looper.prepareMainLooper();
//....
//执行消息循环
Looper.loop();
}
Looper.java
@Deprecated
public static void prepareMainLooper() {
//创建不允许退出Looper
prepare(false);
//同步锁保证只有一个线程
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//将创建的looper实例赋值给sMainLooper,其专门为UI线程保留,只要某个线程调用了prepareMainLooper方法
sMainLooper = myLooper();
}
}
/**
*quitAllowed 表示是否允许Looper运行时退出
**/
private static void prepare(boolean quitAllowed) {
//Looper只能执行一次
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//把创建Looper实例,并且把实例looper存放到TLS中
sThreadLocal.set(new Looper(quitAllowed));
}
// 获取当前线程TLS区域的Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到,线程调用了prepareMainLooper
,就代表它成了一个UI线程,其内部调用的prepare()
来创建Looper
实例,当要获取创建Looper
实例时,是通过myLooper
拿到,也是就get - ThreadLocal
拿
2、ThreadLocal创建
线程本地存储区(Thread Local Storage,简称TLS),每个线程都有自己的私有本地存储区域,不同线程之间彼此不能访问对方的TLS区域
public class ThreadLocal<T> {
//有省略...
//获取当前线程TLS区域的数据
public T get() {
Thread t = Thread.currentThread();
//用了Map存放
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
//将value存储到当前线程的TLS区域。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//获取内部内ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//Map集合
static class ThreadLocalMap {
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
//有省略..
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
// Android-changed: Use refersTo()
if (e != null && e.refersTo(key))
return e;
else
return getEntryAfterMiss(key, i, e);
}
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
}
}
可以看到类型T
泛型,所以这里存储是Looper
泛型,sThreadLocal
的get() / set()
操作的类型都是Looper
类型
ThreadLocal
的作用:每个线程只能保存一个Looper
,不同的线程Looper
是不同的,这样通过调用Looper.perpare(false)
,UI线程中就保存了它对应的唯一的Looper
实例
当在UI线程中调用Looper.myLooper
就返回了UI线程关联的Looper
实例
子线程中如果想获取UI线程关联的Looper
实例,就需要调用getMainLooper
方法
区别:ui线程的looper
是不允许退出,而我们一般创建的looper
是允许退出
Looper.java
public static void prepareMainLooper() {
//不允許
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static void prepare() {
//允许
prepare(true);
}
3、MessageQueue创建
在调用Looper
的prepare
方法就会创建Looper
的实例,同时会把Looper
实例通过ThreadLoca
保存到线程中,在创建Looper
时还会同时创建MessageQueue
Looper.java
private Looper(boolean quitAllowed) {
//创建MessageQueue对象, * 是否退出传递进去
mQueue = new MessageQueue(quitAllowed);
//获取当前线程
mThread = Thread.currentThread();
}
MessageQueue.java
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//native层init,返回mPtr指针通信
mPtr = nativeInit();
}
MessageQueue
的构造中通过Native
方法在native
层创建一个属于NativeMeesageQueue
的消息队列,然后把NativMessageQueue
的地方返回到Java层
保存在mPtr中,java层与native层
之间通信就是通过mPtr
指针
Looper.java
//获取meesageQueue对象
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
通过Looper
的myQueue
方法就能获取到它关联的MessageQueue
实例
4、消息轮询
可以看到在UI线程中,在ActivityThread的mian
方法在创建Looper
后,通过Looper.loop方法就启动了消息循环,这个函数会不断的从MessageQueue
中取出消息、处理消息
Looper.java
@SuppressWarnings("AndroidFrameworkBinderIdentity")
public static void loop() {
//获取looper
final Looper me = myLooper();
//...
for (;;) {
//返回false 退出循环
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
@SuppressWarnings("AndroidFrameworkBinderIdentity")
private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {
//重点❤ 从Looper中取出MessageQueue
Message msg = me.mQueue.next();
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
//....
try {
//重点❤ Message.target 就是对应的Handler.dispatchMessage回调消息
msg.target.dispatchMessage(msg);
}
//....
//重点❤,回收消息
msg.recycleUnchecked();
return true;
}
可以看出在Loop()中是一个死循环,通过looper.mQueue.next()
来获取出最新的消息,当没有消息返回false
进行跳出循环条件
Looper.java
public void quit() {
mQueue.quit(false);
}
//quitSafely和quit方法的区别是,quitSafely方法会等MessageQueue中所有的消息处理完后才退出,而quit会直接退出
public void quitSafely() {
mQueue.quit(true);
}
Looper的quit()
或quitSafely()
被调用,从而调用MessageQueue
的quit()
来通知消息队列退出
5、出队列
MessageQueue的 next()
是最关键的函数
@UnsupportedAppUsage
Message next() {
//mPtr是在构造中被赋值,是指向native层的MessageQueue
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
/**
*重点❤ 死循环,里面分为两部分:
* 1、处理java层消息
* 2、如果没有消息处理,执行IdleHandler
**/
for (;;) {
//处理native层消息,是一个阻塞操作 等待nextPollTimeoutMillis时间后唤醒, messageQueue被唤醒
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//mMessages表示取出java层队列的第一个消息
Message msg = mMessages;
//遇到同步屏障 msg.target为null的消息
if (msg != null && msg.target == null) {
//在do-while中找到异步消息,优先处理异步消息,其中异步消息时候,isAsynchronous = true
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//有消息
if (msg != null) {
//消息还没到触发时间
if (now < msg.when) {
//设置下一次轮询的超时时长(等待时长)
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//消息到达触发时间
mBlocked = false;
//从mMessages的头部获取一条消息并返回
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
//设置消息的使用状态,即flags |= FLAG_IN_US
msg.markInUse();
//返回这个消息
return msg;
}
} else {
//msg == null,没有消息
//设置nextPollTimeoutMillis为-1,准备进入阻塞,等待MessageQueue被唤醒
nextPollTimeoutMillis = -1;
}
//调用了quit方法
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//没有IdleHandler需要处理,可直接进入阻塞
mBlocked = true;
continue;
}
//有IdleHandler需要处理
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//把mIdleHandlers列表转成mPendingIdleHandlers数组
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//遍历mPendingIdleHandlers数组
for (int i = 0; i < pendingIdleHandlerCount; i++) {
//取出IdleHandler
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
//执行IdleHandler的queueIdle方法,通过返回值由自己决定是否保持存活状态
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
// 不需要存活,移除
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
//重置pendingIdleHandlerCount和nextPollTimeoutMillis为0
pendingIdleHandlerCount = 0;
//当=0:表示下一次循环会马上从Messages中取出一个Message判断是否到达执行时间
nextPollTimeoutMillis = 0;
}
}
消息处理整理:
-
Part1
先执行nativePollOnce() ,组赛队列,其中的nextPollTimeoutMillis
表示下一次等待的超时时长,当nextPollTimeoutMillis = 0
或者达到对应的时间值,那么它就会立即返回,
- 当
nextPollTimeoutMillis = -1
表示在队列MessageQueue
中没有消息,会一直等待下去,直到Hanlder
发送消息到队列中,执行nativeWake()
后,MessageQueue
被唤醒,nativePollOnce
就会返回,但它此时并不是立即返回,会先处理native
层的消息后,再返回,然后获取java
层的消息处理
-
nex()
会从mMessages
链表的表头中获取一个消息,首先判断它是否同步屏障,区分条件为msg.target = null 的 Message
- 同步屏障:优先处理异步消息,异步消息在执行顺序上会比同步消息优先执行,
Message.setAsynchronous=true
设置异步消息,常用的消息为同步消息
- 同步屏障:优先处理异步消息,异步消息在执行顺序上会比同步消息优先执行,
- 执行时间
if(now == msg.when)
如果达到执行数据,next()
就会返回这条消息给Looper
处理,并且从链表中删除,如果没有到执行时间就设置nextPollTimeoutMillis
时间等待时长
总结
- 创建
Looper
时候,只能通过Looper
的prepare()
方法创建,在创建Looper
时会内部创建一个MessageQueue
,并且把Looper
保存在线程的ThreadLocal中对应(Map),一个线程只能对应一个Looper,一个Looper只能对应一个MessageQueue
- 在创建
MessageQueue
时候,MessageQueue
与NativeMessageQueue
建立连接,NativeMessageQueue
存储地址位于MessageQueue的mPtr
字段中,java层与native通过mPtr字段进行通信。
-
ThradLocal
的作用,Looper
属于某个线程,而MessageQueue
存储在Looper
中,所以MessageQueue
通过Looper
特定的线程上关联,而Handler
在构造中又与Looper
和MessageQueue
相互关联,通过Handler
发送消息的时候,消息就会被插入到Handler
关联的MessageQueue
中,而Looper
会不断的轮询消息,从MessageQueue
中取出消息给相应的Handler
处理,所有最终通过Handler
发送的消息就会被执行到Looper
所在的线程上,这就是 Handler线程切换的原理,无论发送消息Handler对象处于什么线程,最终处理消息都是在Looper所在的线程。
-
Looper
从MessageQueue
中取出消息后,会交给当前消息的msg.target(Hanlder)
处理,在Handler
中处理消息的回调优先级为:Message的CallBack > Handler的CallBack > Handler的handleMessage方法
- APP在启动的时候
ActivityThread.main()
方法中的Looper.prepareMainLooper()
中已经调用Looper.prepare(false)
,所以在主线创建Handler
无需我们手动调用Looper.prepare()
,而在子线程中,如果我们不传递UI线程所属的Looper
去创建Handler
,那么就需要调用Looper.prepare()
后再创建Handler
传递消息,主要是因为 Handler 要和某个线程中的MessageQueue 和 Looper 关联,只有调用 Looper.prepare()方法,Looper和MessageQueue才属于某个线程。
- 消息池是一个单链表,复用池Message时候,从头部取出,如果取不到,则新建返回,回收Message时候,也是从头插入。
网友评论