Handle 知识点总结
- 基于 android 29
文章目录
Message
主要内部成员
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
int flags;
public long when;
Bundle data;
Handler target;
Runnable callback;
Message next;
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
构造消息
-
new Message()
通常情况下不推荐用这种方式,因为这可能导致产生大量的 Message 实体,不仅会占用内存还会给 GC 造成功压力
-
Message.obtain()
这是官方推荐的构造消息的方法,它可以从 sPool 中复用之前用过的 Message,以达到减少对象创建的效果,可以有效的提升效率。
消息回收
-
如果直接调用了 handleMessage 或 dispatchMessage,则需要在调用后调用如下方法进行消息的回收
public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); }
-
在回收消息时会调用 recycleUnchecked 方法,若消息池(sPool)中的消息数量小于 MAX_POOL_SIZE 也就是 50 个时,该消息才会被回收到 sPool 中
void recycleUnchecked() { flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = UID_NONE; workSourceUid = UID_NONE; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
MessageQueue
主要内部成员
private final boolean mQuitAllowed;
private long mPtr;
Message mMessages;
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuitting;
private boolean mBlocked;
private int mNextBarrierToken;
消息出队(Looper.loop() 的死循环不会卡死的原因)
- 消息出队其实就是返回消息链表的头节点
- 在消息出队前会先判断头节点 Message 的 when 是否大于当前时间,若大于会设置 nextPollTimeoutMillis 为需要等待的时间,当下次循环时 nativePollOnce 方法会等待到该时间再执行
- 当消息队列中没有消息时,会将 nextPollTimeoutMillis 设为 -1,则在下次循环时 nativePollOnce 方法将会无限期等待,直到有方法将其唤醒(nativeWake)
Message next() {
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) {
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 {
// 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);
msg.markInUse();
return msg;
}
} else {
// 下次循环进入 nativePollOnce 时会无限等待
nextPollTimeoutMillis = -1;
}
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);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
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;
}
}
-
这里有一个重要的 jni 方法 nativePollOnce 其有如下作用
- 对发生事件的所有文件描述符调用回调。
- 若超时时间大于零,则等待该超时时间或有事件唤醒。
- 如果超时为零,立即返回而不等待。
- 如果超时为负,则无限期等待,直到有事件唤醒为止。
该方法定义在 /frameworks/base/core/jni/android_os_MessageQueue.cpp 中
其实际会调用到 /system/core/include/utils/Looper.h 的 pollOnce 方法,这里主要看下该方法的注释
详细实现如果感兴趣的小伙伴可以在 /frameworks/base/native/android/looper.cpp 中进一步跟踪查看
/** * Waits for events to be available, with optional timeout in milliseconds. * Invokes callbacks for all file descriptors on which an event occurred. * * If the timeout is zero, returns immediately without blocking. * If the timeout is negative, waits indefinitely until an event appears. * * Returns POLL_WAKE if the poll was awoken using wake() before * the timeout expired and no callbacks were invoked and no other file * descriptors were ready. * * Returns POLL_CALLBACK if one or more callbacks were invoked. * * Returns POLL_TIMEOUT if there was no data before the given * timeout expired. * * Returns POLL_ERROR if an error occurred. * * Returns a value >= 0 containing an identifier if its file descriptor has data * and it has no callback function (requiring the caller here to handle it). * In this (and only this) case outFd, outEvents and outData will contain the poll * events and data associated with the fd, otherwise they will be set to NULL. * * This method does not return until it has finished invoking the appropriate callbacks * for all file descriptors that were signalled. */ int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData); inline int pollOnce(int timeoutMillis) { return pollOnce(timeoutMillis, nullptr, nullptr, nullptr); }
next 方法中的等待机制使得 Message 的 next 方法可以释放 CPU 的资源,因此 Looper.loop() 的死循环不会卡死
消息入队(唤醒等待的线程)
- 消息入队前会先进行以下判断
- 该消息的 target(Handler) 是否有设置
- 该消息是否正在被使用
- mQuitting 是否为 true(是否调用了 quit 方法)
- 消息入队的位置会通过以下判断
- 当插入的消息的 when 为 0 时:直接插入到消息队列头
- 当插入的消息的 when 比消息头的 when 小时:插入到消息队列头
- 当插入的消息的 when 比消息头的 when 大时:遍历消息队列找到比插入的消息 when 大的那个消息,并把要插入的消息插入到该消息的前面
- 在消息入队结束后会判断是否需要唤醒等待的线程,若需要唤醒会调用 nativeWake 方法
boolean enqueueMessage(Message msg, long when) {
// 判断是否有目标 Handler
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) {
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;
prev.next = msg;
}
// 若为 true,则会唤醒之前因 nativePollOnce 导致的等待
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
-
这里有个重要的 jni 方法 nativeWake,其主要就是为了唤醒之前等待的线程
该方法定义在 /frameworks/base/core/jni/android_os_MessageQueue.cpp 中
其实际会调用到 /system/core/include/utils/Looper.h 的 wake 方法,这里主要看下该方法的注释
详细实现如果感兴趣的小伙伴可以在 /frameworks/base/native/android/looper.cpp 中进一步跟踪查看
/** * Wakes the poll asynchronously. * * This method can be called on any thread. * This method returns immediately. */ void wake();
enqueueMessage 中的唤醒机制要使 Message 的 next 方法不会无限的等待下去,因为一旦有新的消息要处理就会唤醒
ThreadLocale
ThreadLocale 是一个线程内部的数据存储类,它可以在方法调用的线程中储存数据,在储存后只有通过该线程才能够取到存储的数据。
设置数据
-
可以在创建 ThreadLocale 时可以重写 initialValue 方法来设置默认的初始化数据。该方法会在 get 方法发现其还未设置数据时调用 setInitialValue 中调用。
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
-
可以通过 set 方法直接设置值
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
可以看到不管是 setInitialValue 还是 set 方法最后都调用了 createMap 方法。其实 Thread 类中的成员变量就有 ThreadLocalMap,该方法就是为该线程的 ThreadLocalMap 赋值
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
获取数据
获取数据可以调用 get 方法,get 方法会先获取当前线程的 ThreadLocalMap 判断是否是空,若是空的则会调用 setInitialValue 给其赋值
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();
}
移除数据
移除数据可以调用 remove 方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
Looper
主要内部成员
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;
private static Observer sObserver;
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
private long mTraceTag;
private long mSlowDispatchThresholdMs;
private long mSlowDeliveryThresholdMs;
初始化 Looper
-
Looper.prepare():
通常在子线程中使用 Handler 前调用,因为在 Handler 的构造器中会调用 Looper.myLooper() 获取当前线程的 Looper 给 mLooper 赋值,若当前线程还没调用过 Looper.prepare(),则会抛出 如下异常
throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()");
而当该线程重复初始化时则会抛出 “Only one Looper may be created per thread” 异常
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)); }
在给当前线程的 ThreadLocale 赋值时会调用 Looper 的构造器构造一个新的 Looper 对象,该 Looper 会在构造时初始化 MessageQueue
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
-
Looper.prepareMainLooper():
该方法会在当前线程初始化 Looper,并将该 Looper 设置为主 Looper,该方法通常
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
如何获取一个 Looper
由于 Looper 的构造器是私有的,所以可以通过下面的方法获取一个 Looper
-
Looper.getMainLooper():
该方法会返回主线程中的 Looper
public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }
-
Looper.myLooper():
该方法会获取当前线程的 Looper,若该线程未调用 Looper.prepare(),则会返回 null
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
消息循环
- 在消息循环开始前会先判断当前线程是否有 Looper,这可以防止在其它线程调用了该方法
- 接下来会进行无限循环并调用 MessageQueue 中的 next 方法取消息,知道取到的消息为 null 时,说明消息队列已经退出了。
- 在取到消息后会获取 Message 中的 target(Handler) 成员,并调用其 dispatchMessage 方法,将该消息回调出去处理。由于该方法所在的线程是调用 Looper.prepare() 方法的线程,因此实现了线程间的通信
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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
for (;;) {
// 该方法在消息还没有到延迟时间或者没有下个消息时是阻塞的
Message msg = queue.next();
if (msg == null) {
// 说明消息队列已经退出了
return;
}
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
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 {
// 将该 Message 分发到其目标 Handler 中处理
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);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",msg)) {
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
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();
}
}
Handler
主要内部成员
private static Handler MAIN_THREAD_HANDLER = null;
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
// 用于 Messenge 的跨进程通信
IMessenger mMessenger;
处理消息
-
在创建 Handler 的时候传入 Handler.Callback,此时若收到消息会回调 handleMessage
Handler handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(@NonNull Message msg) { return false; } });
-
重写 Handler 中的 handleMessage 方法。要注意若已经给 Handler 设置了 Callback,并且其返回了 true,则此方法不会回调
Handler handler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); } };
-
在发送 Message 时给其设置 callback,则消息会直接回调到 Message 的 Callback 中,不过你不可以直接使用 Message 的 setCallback 方法,因为其属于系统隐藏 Api。你可以使用如下方法。
public static Message obtain(Handler h, Runnable callback) { Message m = obtain(); m.target = h; m.callback = callback; return m; }
-
你可以同步调用 dispatchMessage 或 handleMessage 方法,但此时消息处理的就会在你调用的线程中了,并且在调用完后最好要调用 Message 的 recycle 方法使其回收。
public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
发送消息
Handler 中所有发送消息的方法最终都会调用 sendMessageAtTime
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);
}
最后会调用 enqueueMessage 将消息添加到消息队列 MessageQueue 中
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);
}