1. Handler 概述
1.1 Handler 机制
Android Handler机制用于在不同线程之间进行通讯,进行异步音讯处理。
1.2 Handler 机制的组件
- Handler:用于发送和处理音讯或 Runnable 目标。
- Message:音讯目标,在线程之间传递音讯,能够带着少数信息,能够包括数据和在指守时刻运转使命的指令。线程间内存共享。
- MessageQueue:音讯行列,用于存储由 Handler 发送的音讯。每个线程中只会存在一个 MessageQueue 目标。
- Looper:担任维护一个音讯循环(Message Loop),从 MessageQueue 中循环读取音讯,并将他们分配给对应的 Handler 进行处理。每个线程只要一个 MessegeQueue 目标。
1.3 Handler 的运用场景
- 在子线程更新 UI:当后台线程完成一些使命后,需求在UI线程更新UI元素时,能够运用Handler将更新操作传递给UI线程。
- 履行推迟使命:需求在未来某个时刻点或推迟一段时刻后履行使命时,能够运用Handler的postDelayed()办法来实现。
- 守时使命:需求守时履行某些使命,例如守时刷新数据或履行轮询操作时,Handler能够经过postDelayed()办法实现守时使命。
- 线程间通讯:在不同的线程之间传递音讯或履行特定的使命,Handler能够作为线程间通讯的桥梁。
- 处理音讯行列:需求对音讯进行排队处理,依据优先级履行使命时,能够运用Handler的音讯机制来实现。
2. 一个线程有几个 Handler、几个 Looper、几个 MessageQueue?
一个线程能够有多个 Handler 实例。每个 Handler 担任不同类型的音讯或处理不同的使命,供给了更灵敏的音讯处理机制。
一个线程只要一个 Looper、一个 MessageQueue。同一线程中的每一个 Handler 实例都与同一个 Looper 有关。一个线程只要一个 Looper 的规划是为了保持音讯行列的一致性,以保证音讯的有序处理,一个线程通常只要一个音讯行列是为了防止混杂和供给明确的线程间通讯机制。
从代码的角度看一下 Looper 目标的创立,Looper 的构造办法是私有的:
// 本文代码依据Android api 33
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
能够经过 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));
}
3. Handler 的作业流程
3.1 子线程发送音讯(Message)目标 msg
Handler 的一种运用场景,运用 Handler 发送接收音讯:
Handler handler = new Handler(getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
if ( msg.what == 1)
Log.i("MainActivity", "msg: " + (String) msg.obj);
return false;
}
});
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "Hello, Handler";
handler.sendMessage(msg);
}
}).start();
Handler 类中供给了很多调度音讯的办法:
以 send
最初的办法,除了 sendMessageAtFrontOfQueue()
办法之外,其他办法终究都会调用到 sendMessageAtTime()
办法:
若没有调用 sendMessageDelayed()
办法,推迟时刻就为0。
以 post
最初的办法,发送的内容为 Runnable 目标,终究也会调用到 send
最初办法,发送 message。
以 post(Runnable r)
为例:
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
都会经过 getPostMessage()
办法创立一个对应的 Message 目标,然后再调用 send
办法:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
3.2 将音讯目标增加到 MessageQueue 中
接下来将参数传入 Handler 的入队办法 enqueueMessage()
中:
// Handler.java
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);
}
第三行的 msg.target
中的 target 是 Message 类中的一个 Handler 目标。msg.target
在 Message 目标中记录了该 msg 是由哪一个 Handler 目标宣布的,比及分发音讯的时候,经过 target 再分发回对应的 Handler 目标。
MessageQueue 是一个音讯行列,供给了入队和出队的办法,将一切收到的音讯以行列的方式排列,接着经过 queue 调用 MessageQueue 中的 enqueueMessage()
办法:
// MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
// 发送 msg 的 handler 目标是否为 null
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;
}
MessageQueue 运用 mMessages 保存当时待处理音讯。该入队办法将一切音讯按照发送音讯的时刻进行排序。
依据时刻的顺序调用 msg.next
,从而为每一个音讯指定它的下一个音讯是什么。这时会把 mMessages 赋值为新入队的这条音讯,然后将这条音讯的 next 指定为方才的 mMessages,这样也就完成了增加音讯到行列头部的操作。
3.2 主线程取音讯,处理音讯
主线程 main 函数,ActivityThread main()
,主要做两件事情:
Looper.prepareMainLooper();
Looper.loop();
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
}
来看一下 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.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// 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();
// 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);
me.mSlowDeliveryDetected = false;
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
主要看一下第28行的死循环,不断调用 loopOnce()
办法,接下来看一下 Looper 中的 loopOnce()
办法:
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {//主线程不会使msg为空
// No message indicates that the message queue is quitting.
return false;
}
...
try {
msg.target.dispatchMessage(msg);
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);
}
}
...
}
该办法中第3行调用的 MessageQueue 的 next()
办法:
next()
办法为出队办法。假如当时 MessageQueue 中存在 mMessages (即待处理音讯),就将这个音讯出队,然后让下一条音讯成为 mMessages,不然就进入一个阻塞状态,一直比及有新的音讯入队。
next()
回来的音讯目标会赋给 msg:Message msg = me.mQueue.next();
回过头接着看 loopOnce()
办法,第12行每当有一个音讯出队,就将它传递到 msg.target
的 dispatchMessage()
办法中。接下来看一下 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);
}
}
第2行判别 Message 是否设置了 callback,假如设置了则调用,不然持续履行。
Message 能够经过 setCallback 办法设置 callback,但并不引荐运用:
/** @hide */
@UnsupportedAppUsage
public Message setCallback(Runnable r) {
callback = r;
return this;
}
第5行,假如 mCallback 不为空,则调用 mCallback 的 handleMessage()
办法,mCallback 能够在创立 Handler 目标时设置。不然直接调用 Handler 的 handleMessage()
办法,并将音讯目标作为参数传递曩昔。