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;
}
【参阅链接】
Handler的学习总结