文章目录
前言
整理Android消息机制,帮助自己梳理Android消息机制的内容
一、Android消息机制的构成
Android消息机制其实就是Handler()的运行机制,我们开发过程中常常用Handler()和其他任务进行交互,如开启一个子线程完成从一个数据库中拿出数据,而后通知主线程进行UI更新,这时候便需要用到Handler()将子线程的消息交给主线程进行处理;Handler()其实便是Android消息机制与上层的接口,我们要了解Android消息机制,便是了解Handler()的底层实现
Handler()的底层机制
- MessageQueue消息队列,存储一组消息,然后以队列的形式对外提供插入和删除的操作,说白了就是存储待处理的消息,但是MessageQueue的底层实现并不是队列,而是单链表的形式
- Looper,扮演操作员的角色,MessageQueue只是一个存储待处理消息的数据结构,Looper以无线循环的形式去查找是否有新消息要处理,有就处理,没有就等待
- ThreadLocal,ThreadLocal并不是线程,而是一个可以在多个线程间互不干扰地提供数据的类,同时ThreadLocal可以轻松地获取到每个线程的Looper;同时要注意的是,默认子线程是没有Looper的,如果要在子线程中使用Handler,就必须要为子线程创建Looper,主线程(ActivityThread)->UI线程在被初始化时会初始化Looper,也就是默认可以在主线程使用Handler的原因
因而Android消息机制其实就是Handler的运行机制以及Handler所附带MessageQueue和Looper的工作过程
二、为什么只允许主线程对UI进行更新操作
Android UI控件并不是线程安全,在多线程中并发访问容易导致UI控件处于不可预期的状态,因而采用单线程模型(主线程)对UI进行更新操作
三、消息机制具体分析
ThreadLocal原理分析
ThreadLocal是一个线程内部的数据存储类,通过它可以向指定的线程存储数据;数据存储以后,只有在指定的线程才可以获取到数据,而同一个ThreadLocal对象,是可以在不同的线程生成数据副本的,也就是说,你将同一个ThreadLocal对象传入不同的线程,通过get()方法返回的结果是不相同的
而不同的线程,存有不同的Looper对象,对于Handler来说,需要获取到当前线程的Looper,通过一个ThreadLocal类便可轻松获取到当前线程的Looper对象(不同的线程Looper对象有所不同)
为了搞清楚为什么同一个ThreadLocal对象在不同的线程中存有副本,我们来看看在ThreadLocal对象的get()和set()方法源码源码
注:在不同的线程中,调用同一个ThreadLocal对象的set()方法,设置不同的值;而后在不同的线程中调用ThreadLocal的get()方法,返回值是不同的
- 先看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);
}
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
来看这个set流程,一个ThreadLocal调用set方法经历的流程大致如下:
- 获取到当前所处的线程
- 通过当前线程,拿到当前线程内部的threadLocals存储,threadLocals是一个ThreadLocalMap静态类,这里是内部定义了一个继承自WeekReference的类,整体就理解为一个特殊的Map类就可以了,其存储的键值对key是线程,value是某个线程对应的值
- 如果map不为空,调用map的set()方法,将线程-值键值对放入ThreadLocalMap类;为空,创建map,将值放入
再来看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();
}
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()方法,get()方法就更简单啦
- 获取当前线程
- 获取到当前线程存储的ThreadLocalMap(其实是个静态类,哪里获取都一样)
- 将当前线程作为key传入,得到value
- return 值;
- 如果map为空,那么进行初始化,并将进行初始化的值进行返回,具体返回什么值要看ThreadLocal泛型实现了什么
既然每个Thread作为key可以拿出相对应的唯一值,那么试想,每个线程是不是可以扔入唯一的Looper类呢?
MessageQueue
MessageQueue主要对应两个操作,插入和删除:
- enqueueMessage 往队列中插入一条消息
- next 从队列中拿出一条消息,并将其从队列中进行删除
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) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
@UnsupportedAppUsage
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; // -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) {
// 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);
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;
}
}
来看看enqueueMessage方法
- 检验合法性输入
- 检验当前MessageQueue是否处于退出状态
- 同步操作,保证线程安全:一个是给MessageQueue上锁,一个是markInUse()进行标记
- 如果不为空,执行单链表的头插法操作
- 如果为空,进行创建插入,而后唤醒队列
next方法
- 使用了一个for造成无限循环,当队列中存在消息的时候就return消息跳出循环,同时将这条消息移出(prev.next=msg.next),msg.target是发送这条消息的Handler对象;如果不存在消息的时候,则会调用方法进行阻塞
Looper
Looper扮演消息循环的角色,不停地从MessageQueue中查看消息,如果有新消息就立刻执行,否则就会阻塞在那里
以下展示其中重要部分源码
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
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));
}
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
@SuppressWarnings("AndroidFrameworkBinderIdentity")
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;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
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);//分发了
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 (me.mSlowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
me.mSlowDeliveryDetected = 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();
return true;
}
下面我们对其进行分析
- 构造函数中创建了消息队列,而后获取到了当前的线程
- prepare()方法中创建了一个新的Looper,并且扔进了sThreadLocal这个变量,然后我们在源码中查证到这个变量就是ThreadLocal,那么也就是说,我们之前验证的,以当前线程作为key,Looper对象作为value值的做法完全行得通,这在前文已经进行了说明
- loop()方法本质也是一个死循环,传入Looper本身,一个long的数值,一个int的数值;调用loopOnce方法,当返回值为false时,退出
- loopOnce()方法,无非就是取出消息队列的一条消息,然后判断是否为空,当空的时候返回false,外部的loop方法结束,不为空的时候,让msg.target(Handler对象)进行分发;但是事实上next()方法其实是个阻塞方法,当消息队列为空的时候,它并不会返回null,只会阻塞,什么时候消息队列返回的msg为空呢,那便是Looper调用了quit()方法,强制msg为null,进行退出
Handler
下面来看看Handler的工作原理
先来看看sendMessage()
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
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);
}
无非就是几步曲
- 查看是否需要延时操作
- 创建消息,设置消息的target为自己
- 插入到消息队列中
而后MessageQueue的next()方法开始工作,然后交一条消息给Looper,而后Looper开始处理,然后调用dispatchMessage()的方法
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
handleCallback()其实调用msg的callback对象,其实就是我们使用Handler的过程中添加的那个Runnable对象,如果这一步完成了就return,如果没有,那么我们调用handleMessage,这一步其实就是调用我们自己实现的回调,说白了就是我们new Handler(callback)而后重写的那个handleMessage的方法
主线程的消息循环
主线程拥有自己的MessageQueue,通过Looper.prepareMainLooper()创建Looper和MessageQueue,而后looper()方法开启主线程消息循环,内部自己的Handler便为ActivityThread.H,主要负责和ApplicationThread和AMS进行通信
总结
这是目前的Android消息机制的初步整理,笔者针对源码一步步提出了自己的理解,希望帮助到大家