Android的消息机制
Android的消息机制概述
Android的消息机制主要指的是Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作机制
Handler的主要作用是将一个任务切换到某个指定的线程中执行。
它的主要用处就是当要更新UI界面的时候,我们不能在非UI线程进行更改。
为什么不能在非UI线程中更改UI
因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能导致UI控件处于不可预期的状态,
这不很好解决嘛,我们在多线程中学过,直接给它加个锁不就好了
可是这有个问题,那就是会让UI访问的逻辑更加复杂;其次锁机制会降低UI访问的效率
基于这两点,我们选择在UI线程及主线程中修改UI界面
如果我在另一个线程中进行网络请求,获得到的数据要更新在UI界面怎么弄呢?
这时候就要用到Handler了
Handler的流程如下所示:
Handler会调用send/post方法(post方法最终也会调用send方法),在send内部调用MessageQueue的enqueueMessage将Message传入MessageQueue,
Looper会以无限循环的方式进行查找是否有新消息,有的话就处理消息,调用handleMessage,没有的话就进行等待
需要注意的是:
线程默认是没有Looper,如果需要使用Handler就必须为线程创建Looper
但是主线程/UI线程被创建的时候就会初始化Looper,这也是在主线程中默认可以使用Handler的原因
Android的消息机制分析
1.ThreadLocal的工作原理
1.1ThreadLocal的作用
ThreadLocal并不是线程,它的作用是可以在每个线程中存储数据。
可以在不同的线程中互不干扰地存储并提供数据,通过它可以轻松获得每个线程的Looper
1.1.1ThreadLocal的作用场景
1.一般来说当某些数据是以线程为作用域,并且不同线程具有不同的数据副本的时候我们就可以用到ThreadLocal
比如说Looper,它的作用域就是线程,并且不同的线程有不同的Looper
这个时候用ThreadLocal就方便我们实现Looper在线程中的查找
2.另一个运行场景是复杂逻辑下的对象传递,比如监听器的传递,在线程内部只要通过get方法获得监听器
1.1.2ThreadLocal的使用
先定义一个ThreadLocal对象,我们先选择Boolean类型
private ThreadLocal<Boolean>mBooleanThreadLocal = new ThreadLocal<Boolean>();
然后分别在主线程,子线程1,子线程2设置和访问它的值
mBooleanThreadLocal.set(true);
Log.d("TAG","mBooleanThreadLocal="+ mBooleanThreadLocal.get());
new Thread("Thread#1"){
@Override
public void run(){
mBooleanThreadLocal.set(false);
Log.d("TAG","[Thread#1]mBooleanThreadLocal="+mBooleanThreadLocal.get());
};
}.start();
new Thread("Thread#2"){
@Override
public void run(){
Log.d("TAG","[Thread#2]mBooleanThreadLocal="+mBooleanThreadLocal.get());
};
}.start();
在主线程将mBooleanThreadLocal的值为true,子线程1为false,子线程2为null,因为没有设置,主线程为true
虽然是在不同的线程中访问同一个ThreadLocal,但是不同线程中的ThreadLocal所获取到的值是不一样的。
ThreadLocal之所以会有这种效果,是因为不同线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引查找对应的value值
然后我们分别看看ThreadLocal的set方法和get方法,看它们是怎么实现的。
1.1.3ThreadLocal的set方法
public void set(T value) {
Thread t = Thread.currentThread(); //获取当前线程
ThreadLocalMap map = getMap(t); //查找当前线程的本地储存区
if (map != null)
map.set(this, value);//保存数据value到当前线程
else
//当线程本地存储区,尚未存储该线程相关信息时,
//创建ThreadLocalMap本地存储区对象
createMap(t, value);
}
我们刚才说ThreadLocal的内部会从各自线程中取出一个数组,然后再从各自数组中根据ThreadLocal的索引查找对应的value值
然后我们看这段代码:
- 获取当前线程的引用,即调用 Thread.currentThread() 方法获取当前线程对象 t。
- 调用 getMap(t) 方法从当前线程中获取 ThreadLocalMap 对象 map,该对象是线程本地存储区。
- 如果 map 不为 null,则说明当前线程已经拥有 ThreadLocalMap 对象,直接将 value 存储到 map 中。
- 如果 map 为 null,则说明当前线程还没有 ThreadLocalMap 对象,需要调用 createMap(t, value) 方法创建该对象,并将 value 存储到其中。
然后我们看一下它是怎么存进去的
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
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)]) {
// Android-changed: Use refersTo() (twice).
// ThreadLocal<?> k = e.get();
// if (k == key) { ... } if (k == null) { ... }
if (e.refersTo(key)) {
e.value = value;
return;
}
if (e.refersTo(null)) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
这段代码是 Java 中 ThreadLocalMap 类的 set 方法的实现。ThreadLocalMap 是 ThreadLocal 类中用于存储线程局部变量的内部类,它通过哈希表实现。set 方法的作用是将给定的 ThreadLocal 对象和值存储到哈希表中。
这时候我们看看get()方法
1.1.4ThreadLocal的get方法
public T get() {
Thread t = Thread.currentThread();
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();
}
这段代码前面的这部分和set方法一样,获得当前的thread,然后调用 getMap(t) 方法从当前线程中获取 ThreadLocalMap 对象 map,该对象是线程本地存储区。
如果ThreadMap里面的不为空,那么获取当前线程的本地储存区中的Entry,如果Entry不为空,则返回数据
我们看看 ThreadLocalMap.Entry里面的源码
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
1.1.5总结
1.为什么不同的线程访问同一个ThreadLocal获得的值不同?
这是因为每个ThreadLocal对象都有一个内部的ThreadMap对象用于存储对象,当一个线程去访问ThreadLocal对象,ThreadLocal会在自己的ThreadLocalMap对象中寻找对应的值,如果当前线程还没有在ThreadLocalMap存储相应的值,ThreadLocal就会为当前线程创建一个新值,这样,每个线程都拥有自己独立的值副本,互不干扰。
2.为什么不同线程的ThreadLocalMap不一样
ThreadLocalMap
在Java中被设计为每个线程独立维护的数据结构,这是为了解决多线程并发访问时的线程安全性问题。每个线程拥有自己的ThreadLocalMap
实例,它是ThreadLocal
类的一个成员变量。
2.MessageQueue的工作原理
2.1MessageQueue的概念
MessageQueue内部不是队列,而是插入和删除有优势的单链表。MessageQueue是消息机制的Java层和C++层的连接纽带,大部分核心方法都交给native层来处理,
2.2MessageQueue的作用
MessageQueue主要包含两个操作:插入和读取
读取操作的本身会带有删除操作,
其中插入方法对应enqueueMessage
读取方法对应next
其中enqueueMessage的相关源码如下:
2.2.1enqueueMessage源码
boolean enqueueMessage(Message msg, long when) {
// 每一个普通Message必须有一个target
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) {//正在退出时,回收msg,加入到消息池
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) {
//p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
//消息队头存在barrier,并且同时Message是队列中最早的异步消息。
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;
}
//消息没有退出,我们认为此时mPtr != 0
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
前面两个判断不用看,直接进入锁
msg.markInUse();
msg.when = when;
它先标记消息为使用中,然后设置使用时间
然后在p == null || when == 0 || when < p.when的if判断中判断MessageQueue中有没有消息,或者msg的触发时间为队列中最早则进入该分支,然后进行以下3步操作:
1.将给定消息的next
属性设置为当前消息队列的头消息(p
),将给定消息作为新的头消息。
2.更新消息队列的头消息为给定消息。
3.唤醒事件队列
(其实这段if操作是判断消息是不是最先进来的,如果是最先进来的则把它放在头部)
然后如果它的MessageQueue中有消息且msg不是最先进入该链表的,则
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;
第一段代码应该是在判断唤不唤醒,在事件被阻塞或者当前头消息(p
)是否是一个barrier(目标为null
)或者给出的消息是个异步消息的时候则唤醒
然后在在一个无限循环中,遍历消息队列,找到合适的位置将给定消息插入到消息队列中,并更新needWake
标志。
它主要进行的操作是先用pre保存p的值,然后将p指向下一个指针,如果进行完上面的操作后p==null或者给定消息的触发时间小于下一个消息的触发时间则退出循环
如果之前判断需要唤醒事件队列,并且当前消息是异步消息,则将needWake
标志设置为false
,表示不需要唤醒事件队列。
将给定消息的next
属性设置为当前消息指针(p
),并将前一个消息的next
属性指向给定消息
上面的操作是MessageQueue的插入
现在来看看MessageQueue的读取
2.2.2next源码
Message next() {
// Return here if the message loop has already quit and been disposed. This can happen if the application tries to restart a looper after quit which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} 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);
//设置消息的使用状态,即flags |= FLAG_IN_USE
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run. Idle handles only run if the queue is empty or if the first message in the queue (possibly a barrier) is due to be handled in the future.
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;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers. We only ever reach this code block during the first iteration.
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);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
可以发现next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法则会一直阻塞在这里,当有新消息的时候,next方法会返回这条消息,并且将其从单链表中移除
2.2.3总结
enqueueMessage中如果消息队列为空(p为null)或给定消息的触发时间为0或给定消息的触发时间小于头消息的触发时间则把该消息设置为头部
否则就进行单链表的插入操作,同时它也会判断如果被唤醒且处于异步操作的话,则不再唤醒,退出插入操作
next则是无限循环的方法,这一点和Looper很像,如果没有消息则会阻塞,有消息则返回消息,且移除它
3.Looper的工作原理
Looper在Android中扮演消息循环的角色,这一点就和MessageQueue的next很像了,都是无限循环,它的主要作用就是不停地从MessageQueue中查看是否有新消息,如果有新消息就立刻处理,否则就阻塞在那儿
3.1 Looper的构造方法
我们看看它的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建一个MessageQueue
mThread = Thread.currentThread();//将当前线程对象保存起来
}
在Looper的构造方法中我们可以发现,Looper创建了一个MessageQueue,然后用于将当前线程对象保存起来
然后在UI线程即主线程中我们是不用手动创建Looper,它会自动帮我们创建好,但是在非主线程它不会自动帮我们创建Looper,没有Looper的话就无法使用Handler
所以我们看看Looper是如何创建的
3.2Looper的创建
new Thread("Thread#2") {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
};
}.start();
通过Looper.prepare()即可为当前线程创建一个Looper,接着通过Looper.loop()来开启消息循环:
3.2.1prepare()源码
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建Looper对象,并保存到当前线程的ThreadLocalStorage区域
sThreadLocal.set(new Looper(quitAllowed));
}
我们会发现Looper的创建是调用的ThreadLocal的set方法
所以说明Looper的创建与ThreadLocal有关
Looper其实除了prepare方法以外其实还提供了prepareMainLooper方法,这个方法是给主线程创建Looper使用的,本质上也还是通过prepare实现的
我感觉可以继续说:本质上是通过threadLocal实现的
由于主线程的Looper的特殊性,因为它不用手动实现Looper就可以用,所以Looper提供了一个getMainLooper方法,可以在任何地方获得主线程的Looper
同时Looper是可以退出的,它提供了quit和quitSafely两个方法用来退出一个Looper,
区别其实和它们的名字一样,quitSafely更安全一点,
quit是直接退出
quitSafely只是设定一个退出标记,然后把消息队列中已有的消息处理完后才会安全退出
我们来看看它们两个的源码
3.3Looper的退出
public void quit() {//直接退出Looper
mQueue.quit(false); //消息移除
}
public void quitSafely() {//设定一个标记,然后把消息队列中已有消息处理完后才安全退出
mQueue.quit(true); //安全地消息移除
}
我原本以为代码很长,然后就这?
然后我想点击一下quit继续看里面的源码,然后显示Cannot find declaration to go
Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send方法会返回false
在子线程中,如果你完成所有事后要自己手动退出哦,否则子线程就一直处于等待状态
且如果调用了Looper的quit或者quitSafely的任意一个方法,该线程会立刻终止
3.4Looper的查找
Looper的查找其实就是Looper的loop方法
3.4.1loop的源码
public static void loop() {
final Looper me = myLooper();//获取TLS存储的Looper对象
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process, and keep track of what that identity token actually is.
确保该线程的身份是本地进程的身份,并跟踪该身份令牌实际上是什么。
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
允许使用系统道具覆盖阈值。例如adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
// Allow overriding a threshold with a system prop. e.g. adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
for (;;) {//进入loop的主循环方法
Message msg = queue.next(); // might block
if (msg == null) {
//没有消息表示消息队列正在退出。
// No message indicates that the message queue is quitting.
return;
}
这必须在局部变量中,以防 UI 事件设置记录器
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;//默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
确保观察者在处理事务时不会改变。
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
msg.target.dispatchMessage(msg);//用于分发Message
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
一旦我们写了一个慢速传递日志,就压制直到队列耗尽。
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//恢复调用者信息
确保在调度过程中线程的身份没有被破坏。
// Make sure that during the course of dispatching the identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();//将Message放入消息池
}
}
Looper的loop方法的工作过程很好理解,loop方法是一个死循环,唯一跳出循环的方法事MessageQueue的next方法返回了null
还记得MessageQueue的next是干什么的嘛?它是进行查询的,但是如果有新消息显示出来的话,它会一边返回那个新消息,一边移除它
当Looper的quit方法被调用时,Looper
就会调用MeaaageQueue
的quit
或者quitSafely
方法来通知MeaaageQueue
退出,当消息队列被标为退出状态时,它的next
方法就会返回null
,跳出loop循环。
next是一个阻塞操作,当没有消息时,next方法会一直阻塞在那里,这也导致loop方法一直阻塞在那里。如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样Handler发送的消息最终又交给它的dispatchMessage方法来处理了。但是这里不同的是,Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就成功地将代码逻辑切换到指定的线程中去执行了。
在Looper的构造方法中会去保存当前的线程,Looper在哪个线程创建,Handler的dispatchMessage方法终究会在那个线程执行。
简单总结一下,就是Looper是通过MessageQueue的next方法获得要处理的消息,然后调用handler的dispatchMessage方法进行处理,
当looper的quit或者quitSafely方法被调用,则代表Handler要被终止,这时候next给looper返回null,Looper的loop方法结束
4.Handler的工作原理
Handler的工作主要包含消息的发送和接收过程。消息的发送可以通过post的一系列方法以及sendMessage的一系列方法来实现,post的一系列方法最终是通过sendMessage的一系列方法来实现的。
先看看Handler的有参构造
4.1有参构造
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//必须先执行Looper.prepare(),才能获取Looper对象,否则为null.
mLooper = Looper.myLooper();//从当前线程的TLS中获取Looper对象
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//消息队列,来自Looper对象
mCallback = callback;//回调方法
mAsynchronous = async;//设置消息是否为异步处理方式
}
主要就是初始化Looper与进行其他的一些操作
然后调用sendMessage进行传递消息
4.2sendMessage源码
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;
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);
}
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);
}
调用sendMessage后会调用sendMessageDelayed然后调用sendMessageDelayed然后调用MessageQueue的enqueueMessage,这个方法就是MessageQueue的插入的方法
然后之后MessageQueue的next查询方法会被调用,这样Looper的loop也就会开始执行,并执行Handler的dispatchMessage进行处理
然后我们看看dispatchMessage的源码
4.3dispatchMessage源码
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
//检查Message的callback是否为null,不为null(存在回调方法)就通过handleCallback来处理消息。
handleCallback(msg);
} else {
if (mCallback != null) {
//当Handler存在Callback成员变量时,回调方法handleMessage();
if (mCallback.handleMessage(msg)) {
return;
}
}
//Handler自身的回调方法handleMessage()
handleMessage(msg);
}
}
我们发现dispatchMessage主要调用的一个是handleCallback方法还有一个是Handler自身的回调方法handleMessage()
我们看看handleCallback是怎么执行的
4.4handleCallback源码
Message的callback是一个Runnable对象,实际上就是Handler的post方法所传递的Runnable参数
。handleCallback的逻辑也是很简单,如下所示
private static void handleCallback(Message message) {
message.callback.run();
}
其次,msg.callback(就是Handler的post方法所传递的Runnable参数)为null,再检查mCallback是否为null,不为null就调用mCallback的handleMessage方法来处理消息。Callback是个接口,它的定义如下:
public interface Callback {
public boolean handleMessage(Message msg);
}
在日常开发中,创建Handler最常见的方式就是派生一个Handler的子类并重写其handleMessage方法来处理具体的消息,而Callback给我们提供了另外一种使用Handler的方式,当我们不想派生子类时,就可以通过Callback来实现。
Handler处理消息的过程可以归纳为一个流程图
5.主线程消息循环
Android主线程是ActivityThread,主线程的入口方法是main,main方法中会通过Looper.preapareMainLooper()
创建主线程的Looper
以及MesaageQueue
public static void main(String[] args) {
// 初始化Environment
Environment.initForCurrentUser();
// 初始化Event日志
EventLogger.setReporter(new EventLoggingReporter());
// 根据用户Id创建指定目录
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
// 将当前线程作为主线程,初始化Looper
Looper.prepareMainLooper();
// 创建ActivityThread
ActivityThread thread = new ActivityThread();
// 如果是System则直接创建Application,如果非System则将ApplicationThread注册到AM中
thread.attach(false);//建立Binder通道 (创建新线程)
// 初始化Handler
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//消息循环运行
Looper.loop();
// 异常终止,注意这里的异常通常是执行不到的,上方loop有异常也不会执行到这里。
throw new RuntimeException("Main thread loop unexpectedly exited");
}
ActivityThread还有一个Handler,ActivityThread.H
内部定义了一组消息类型,主要包含了四大组件的启动和停止等过程。
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
.........
}
ActivityThread通过它的内部类ApplicationThread和ActivityManagerService进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型。
型。