Android 通过Binder机制来解决进程间通信问题,而通过Handler 消息机制来解决线程之间通信问题,或者用来切换线程。毕竟Android要求只能主线程更新界面,对于耗时操作和网络请求都需要在其他线程执行,就涉及到线程切换。
Android应用都是靠消息来驱动工作的。大致原理就是:
- 一个消息队列,往消息队列投递信息。
- 一个消息循环,不断从消息队列取消息,然后处理。
而这个工作原理就涉及到三个类:Looper、MessageQueue和Handler。
Looper
Looper的创建需要通过Looper.prepare函数来创建。
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");
}
sThreadLocal.set(new Looper(quitAllowed));
}
这里的sThreadLocal对象是ThreadLocal类型,用于存储线程私有全局变量,具体可看ThreadLocal解析。也就是在线程任何地方获取到的Looper对象都是同一个。通过判断语句来看,一个线程中只能调用一次Looper.prepare创建一次Looper对象。通过Looper的构造函数直接创建了Looper对象。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper的构造函数中创建了一个消息队列MessageQueue的实例mQueue,并持有当前线程对象的引用mThread。也就是说prepare函数创建了Looper对象,且该对象持有一个消息队列。并将Looper对象设置到线程ThreadLocal中,保证每条线程只有一个Looper对象。
有了消息队列,就要让消息队列跑起来,在调用了prepare函数,再调用Looper.loop函数,开始消息循环。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
myLooper函数通过ThreadLocal对象sThreadLocal获取Looper对象,所以prepare函数需要在loop函数之前调用,否则会抛出异常。
然后在for死循环中,不断调用消息队列MessageQueue对象queue的next函数来获取下一个待处理的消息Message对象message。这里的消息队列来自Looper对象,即在创建Looper对象创建的MessageQueue对象。
然后通过Message对象的target对象分发处理该消息。最后对消息进行回收处理。那这里的target对象是谁?也就是Handler对象了。
Handler
Handler可以理解为消息机制里的辅助类,因为此时消息队列有了,消息循环也有,我们可以手动去队列取消息和往队里方向,但这容易出错。所以通过Handler来投递信息和处理信息。
Handler一共7个构造函数,其中两个已经标记过时了。
//设置Looper
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
//设置Looper和Callback
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
this(looper, callback, false);
}
//设置同步屏障
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Handler(boolean async) {
this(null, async);
}
//设置回调和同步
public Handler(@Nullable Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
总的看下来,构造函数主要设置三个参数Looper对象,Callback对象,布尔类型async。Callback对象主要涉及消息的处理,后面说,而async表示是否设置同步屏障。通过构造函数了解到Handler的消息队列指向了Looper对象的消息队列对象。
那么如何往消息队列投递信息呢?
Handler投递信息有很多重载函数,但主要通过sendMessage函数投递一个Message对象和post函数投递一个Runable对象。
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);
}
可见对消息Message对象设置延迟时间和同步屏障,并将Handler对象自身设置给了Message对象的target变量,最后调用的消息队列的enqueueMessage队列将消息投递到消息队列中。
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
而post函数只是将Runable对象封装到Message对象中的callback函数。最终还是调用sendMessagedDelayed函数的历程去处理。
从这里我们定位到了在Looper的loop函数中Message对象的target是Handler对象,看看dispatchMessage函数。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
从dispatchMessage函数可以看出对消息处理的优先级。
- 消息
Message对象msg自带的Runable对象callback。也就是我们通过post函数投递的Runable对象会最先被处理。 - 优先级排第二就是我们在创建Handler对象时,设置的全局回调
Callback对象mCallback。 - 优先级最低的,也是最常用的,重载
handleMessage函数,该函数默认是空实现。
MessageQueue
Handler中调用了MessageQueue对象的enqueueMessage函数,将Message对象投递到队列中。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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) {//队列头
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) {
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;
}
enqueueMessage函数主要根据消息处理的时间when,插入到队列合适的位置。
再看一下next函数,如何从消息队里取消息。
Message next() {
//当应用重启该mPtr会被置null
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
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) {
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;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
//当所有挂起消息被处理了,则返回null
if (mQuitting) {
dispose();
return null;
}
//首次运行或者没有消息了
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//没有空闲的消息
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);
}
}
}
//避免重复执行已处理的信息
pendingIdleHandlerCount = 0;
// 重置该时间,这样可以立即处理下一等待的信息
nextPollTimeoutMillis = 0;
}
}
在next函数中,如果有同步屏障,会优先返回异步信息Looper对象,然后进行处理。如果队列中没有消息,则直接处理已被挂起的消息。
异步消息与同步消息
默认情况下,Handler对象会将mAsynchronous设置为false,会把消息Message对象的asynchronous设置为false,表示异步消息。当Message对象的asynchronous为ture表示同步消息。而当target==null,表示这是一个同步屏障。
这三者的作用就是为了处理一个优先级的问题,当同步屏障到来时msg.target==null,优先处理队列中的异步信息。
为什么说msg.target==null是一个同步屏障,通过Handler投递出来的信息msg.target都是不为null的。而发送一个同步屏障的代码。
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
可见,发送一个屏障,并没有设置Handler对象给target变量。而异步信息优先权大于同步信息从何体现?
在MessageQueue类next函数中有这么段代码。
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());
}
当发送一个同步屏障时,需要手动移除该屏障,否则会一直停留在异步消息的处理中,因为MessageQueue的next函数没有移除同步屏障。同步屏障的一个应用例子就在ViewRootImple的scheduleTraversals函数中。
线程切换
Handler的实际应用就是UI线程与其他线程之间的切换。默认情况下,主线程会创建Looper对象,不需要我们手动创建。我们在主线程创建Handler对象时没有传入Looper对象,那默认就使用了主线程的Looper对象。在其他线程投递信息最终也是在主线程处理,达到线程切换的原理。
而RxJava、Glide中网络请求,耗时操作的原理也是使用Handler切回到主线程。
也有面试官会问,能否在子线程创建Handler,答案是可以,前提是要先创建Looper对象,并且开启循环,即调用Looper.preare();Looper.loop()。
HandlerThread
正常情况下,使用主线程的Looper.myLooper函数获取的Looper对象来创建的Handler一般不会有问题。
但如果在使用子线程的Looper对象呢。如下面代码。
public class LooperThread extends Thread {
public Looper myLooper;
@Override
public void run() {
Looper.prepare();
myLooper=Looper.myLooper();
Looper.loop();
}
}
val looperThread = LooperThread()
looperThread.start()
Handler(looperThread.myLooper)
handler.sendEmptyMessage(1)
定义了一个具有Looper对象的线程,这样就可以使用LooperThread对象的Looper对象。但上面代码Handler(looperThread.myLooper)有问题,可能此时Looper对象还没初始化。因此需要采用同步机制来处理该问题,不过Android已经帮我们定义了好了HandlerThread来出来该问题了。
其run函数的实现
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
getLooper函数的实现
public Looper getLooper() {
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
wasInterrupted = true;
}
}
}
return mLooper;
}
【参考链接】