消息机制中主要用于多线程的通信,在 Android 开发中最多见的使用场景是:在子线程作耗时操做,操做完成后须要在主线程更新 UI(子线程不能直接修改 UI)。这时就须要用到消息机制来完成子线程和主线程的通信。java
如如下代码片断所示:数据结构
public class MainActivity extends AppCompatActivity {
private TextView tvText;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tvText.setText(msg.obj.toString());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = (TextView) findViewById(R.id.tv_text);
new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = Message.obtain();
msg.obj = Thread.currentThread().getName();
mHandler.sendMessage(msg);
}
}.start();
}
}复制代码
子线程阻塞 10 秒后发送消息更新 TextView,TextView 显示来源的线程名。多线程
这里有两个限制:app
不能让阻塞发生在主线程,不然会发生 ANR异步
不能在子线程更新 TextView。async
因此只能在子线程阻塞 10 秒,而后经过 Handler 发送消息,Handler 处理获取到的消息并在主线程更新 TextView。ide
private Handler mHandler = new Handler() {
...
};复制代码
查看 Handler 的源码:oop
...
public Handler() {
this(null, false);
}
...
/** * @hide 该构造方法是隐藏的,没法直接调用 */
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
...复制代码
Handler 的构造方法须要传入两个参数,第一个参数是 Handler.Callback 接口的实现,第二个参数是标志传递的 Message 是不是异步。性能
构造方法内部首先会检查 Handler 的使用是否可能存在内存泄漏的问题,若是存在会发出一个警告:优化
因此在使用 Handler 的时候通常声明为静态内部类或使用弱引用的方式。
接着会调用 Looper.myLooper() 获取到 Looper 对象,并判断该 Looper 对象是否为 null,若是为 null 则抛出异常;若是不为 null 则进行相应的赋值操做。由此可知 Looper.myLooper() 方法并不会构造一个 Looper 对象,而是从某个地方获取到一个 Looper 对象。
因此,在建立 Handler 对象时必须先建立 Looper 对象。
下面查看 Looper 源码:
...
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */
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));
}
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
...
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
...
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
...复制代码
从 Looper 的源码可知,Looper 类中对外部提供两个方法用于建立 Looper 对象:prepare() 和 prepareMainLooper(),并将建立的 Looper 对象保存到 sThreadLocal 中。myLooper() 方法获取 Looper 对象也是从 sThreadLocal 中获取。
sThreadLocal 是一个ThreadLocal 对象,ThreadLocal 用于提供线程局部变量,在多线程环境能够保证各个线程里的变量独立于其它线程里的变量。也就是说 ThreadLocal 能够为每一个线程建立一个【单独的变量副本】,至关于一个线程的 private static 类型变量。
在 Looper 的真正建立对象方法 prepare(boolean quitAllowed) 中,会先判断当前线程是否已经有 Looper 对象,没有时才能够建立并保存到当前线程中,每一个线程只容许有一个 Looper。
上面的示例代码中是在主线程中实例化 Handler 的,可是并无调用 Looper 的建立方法,并且也没有抛出异常,说明主线程中是有 Looper 对象的。
在 Looper 的 prepareMainLooper() 方法注释中能够看到这样一句话:
The main looper for your application is created by the Android environment, so you should never need to call this function yourself.
意思是:应用程序的主 Looper 由 Android 环境建立,不该该本身调用该方法。
由此可知,在 Android 系统源码中应该会调用该方法,经过查找该方法的使用发现,在 ActivityThread 类的 main 方法中调用了 Looper.prepareMainLooper():
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}复制代码
因此在应用程序启动时就已经为主线程建立了一个 Looper 对象。
继续分析示例代码,在子线程阻塞结束后会建立一个 Message 对象,而后使用 Handler 发送该 Message 对象。
...
Message msg = Message.obtain();
msg.obj = Thread.currentThread().getName();
mHandler.sendMessage(msg);
...复制代码
调用 Message 的 obtain() 方法建立 Message 对象。
查看 Message 的源码:
...
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
...
public Message() {
}
...复制代码
Message 是消息机制中消息的载体,为了优化性能,避免重复 Message 的建立,Message 使用了消息池机制,当调用 obtain() 方法时,会先尝试从消息池中获取一个 Message 对象,消息池中没有时才建立新的对象;Message 对象使用完后会从新回收到消息池中。Message 的消息池使用了链表的数据结构,Message 类自己时支持链表结构的。
因此在建立 Message 对象时不要直接使用构造方法。
建立好 Message 对象后,能够给 Message 的一些属性赋值,用于描述该消息或携带数据。
调用 Handler 的 sendMessage(Message msg) 方法发送消息。
经过查看 Handler 的源码可知,在 Handler 中有许多发送消息的方法,全部的发送消息方法最终都会调用 enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 方法。
...
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
...
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
...
public boolean sendMessageAtTime(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(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}复制代码
首先给 Message 的 target 属性赋值,即当前的 Handler;而后根据 Handler 的 mAsynchronous 值设置该 Message 是不是异步的,mAsynchronous 的值在 Handler 实例化时被赋值;最后调用 MessageQueue 的 enqueueMessage(Message msg, long when) 方法。
能够看出在 Handler 中也只是对 Message 对象的属性进行了相关赋值操做,最终是调用了 MessageQueue 的 enqueueMessage(Message msg, long when) 方法。
enqueueMessage() 方法中的 MessageQueue 对象来自于 Handler 的 mQueue 属性:
public boolean sendMessageAtTime(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);
}复制代码
而 mQueue 属性在 Handler 实例化时赋值的:
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}复制代码
mQueue 是 Looper 中的 MessageQueue 对象,在 Looper 建立时被实例化:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}复制代码
查看 MessageQueue 的构造方法:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}复制代码
MessageQueue 是消息队列,Handler 发送消息其实就是将 Message 对象插入到消息队列中,该消息队列也是使用了链表的数据结构。同时 Message 也是消息机制中 Java 层和 native 层的纽带,这里暂且不关心 native 层相关实现。
MessageQueue 在实例化时会传入 quitAllowed 参数,用于标识消息队列是否能够退出,由 ActivityThread 中 Looper 的建立可知,主线程的消息队列不能够退出。
MessageQueue 插入消息:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) { // target 即 Handler 不容许为 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(); // 回收 Message,回收到消息池
return false;
}
msg.markInUse(); // 标记为正在使用
msg.when = when;
Message p = mMessages; // 获取当前消息队列中的第一条消息
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 消息队列为空 或 新消息的触发时间为 0 或 新消息的触发时间比消息队列的第一条消息的触发时间早
// 将新消息插入到队列的头,做为消息队列的第一条消息。
msg.next = p;
mMessages = msg;
needWake = mBlocked; // 当阻塞时须要唤醒
} else {
// 将新消息插入到消息队列中(非队列头)
// 当阻塞 且 消息队列头是 Barrier 类型的消息(消息队列中一种特殊的消息,能够看做消息屏障,用于拦截同步消息,放行异步消息) 且 当前消息是异步的 时须要唤醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 循环消息队列,比较新消息的触发时间和队列中消息的触发时间,将新消息插入到合适的位置
for (;;) {
prev = p; // 将前一条消息赋值给 prev
p = p.next; // 将下一条消息赋值给 p
if (p == null || when < p.when) {
// 若是已是消息队列中的最后一条消息 或 新消息的触发时间比较早 则退出循环
break;
}
if (needWake && p.isAsynchronous()) {
// 须要唤醒 且 下一条消息是异步的 则不须要唤醒
needWake = false;
}
}
// 将新消息插入队列
msg.next = p;
prev.next = msg;
}
if (needWake) {
// 若是须要唤醒调用 native 方法唤醒
nativeWake(mPtr);
}
}
return true;
}复制代码
MessageQueue 根据消息的触发时间,将新消息插入到合适的位置,保证全部的消息的时间顺序。
消息的发送已经分析过了,下面须要分析的是如何获取消息并处理消息,继续分析实例代码:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tvText.setText(msg.obj.toString());
}
};复制代码
从示例代码中能够看到 Handler 的 handleMessage(Message msg) 负责处理消息,可是并无看到是如何获取到消息的。须要在 Handler 的源码中查找是在哪里调用 handleMessage(Message msg) 方法的。
经过在 Handler 的源码中查找,发现是在 dispatchMessage(Message msg) 方法中调用 handleMessage(Message msg) 的。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}复制代码
dispatchMessage(Message msg) 方法中,根据不一样的状况调用不一样的消息处理方法。继续向上查找 dispatchMessage(Message msg) 的引用,发现是在 Looper 的 loop() 方法中调用的,而在以前分析 Looper 的建立时,能够知道在 ActivityThread 的 main 方法中有调用 loop() 方法。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}复制代码
下面分析 loop() 方法:
public static void loop() {
final Looper me = myLooper(); // 获取当前线程的 Looper 对象
if (me == null) { // 当前线程没有 Looper 对象则抛出异常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // 获取到当前线程的消息队列
// 清空远程调用端进程的身份,用本地进程的身份代替,确保此线程的身份是本地进程的身份,并跟踪该身份令牌
// 这里主要用于保证消息处理是发生在当前 Looper 所在的线程
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 无限循环
for (;;) {
Message msg = queue.next(); // 从消息队列中获取消息,可能会阻塞
if (msg == null) {
// 没有消息则退出循环,正常状况下不会退出的,只会阻塞在上一步,直到有消息插入并唤醒返回消息
return;
}
// 默认为 null,可经过 setMessageLogging() 方法来指定输出,用于 debug 功能
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// 开始跟踪,并写入跟踪消息,用于 debug 功能
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
// 经过 Handler 分发消息
msg.target.dispatchMessage(msg);
} finally {
// 中止跟踪
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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(); // 回收消息,将 Message 放入消息池
}
}复制代码
在 loop() 方法中,会不停的循环如下操做:
调用当前线程的 MessageQueue 对象的 next() 方法获取消息
经过消息的target,即 Handler 分发消息
回收消息,将分发后的消息放入消息池
在 loop() 方法中获取消息时有可能会阻塞,来看下 MessageQueue 的 next() 方法的实现:
Message next() {
// 若是消息队列退出,则直接返回
// 正常运行的应用程序主线程的消息队列是不会退出的,一旦退出则应用程序就会崩溃
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // 记录空闲时处理的 IdlerHandler 数量,可先忽略
int nextPollTimeoutMillis = 0; // native 层使用的变量,设置的阻塞超时时长
// 开始循环获取消息
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 调用 native 方法阻塞,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会中止阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 尝试获取下一条消息,获取到则返回该消息
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages; // 获取消息队列中的第一条消息
if (msg != null && msg.target == null) {
// 若是 msg 为 Barrier 类型的消息,则拦截全部同步消息,获取第一个异步消息
// 循环获取第一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 若是 msg 的触发时间尚未到,设置阻塞超时时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获取消息并返回
mBlocked = false;
if (prevMsg != null) {
// 若是 msg 不是消息队列的第一条消息,上一条消息的 next 指向 msg 的 next。
prevMsg.next = msg.next;
} else {
// 若是 msg 是消息队列的第一条消息,则 msg 的 next 做为消息队列的第一条消息 // msg 的 next 置空,表示从消息队列中取出了 msg。
mMessages = msg.next;
}
msg.next = null; // msg 的 next 置空,表示从消息队列中取出了 msg
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse(); // 标记 msg 为正在使用
return msg; // 返回该消息,退出循环
}
} else {
// 若是没有消息,则设置阻塞时长为无限,直到被唤醒
nextPollTimeoutMillis = -1;
}
// 若是消息正在退出,则返回 null
// 正常运行的应用程序主线程的消息队列是不会退出的,一旦退出则应用程序就会崩溃
if (mQuitting) {
dispose();
return null;
}
// 第一次循环 且 (消息队列为空 或 消息队列的第一个消息的触发时间尚未到)时,表示处于空闲状态
// 获取到 IdleHandler 数量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 没有 IdleHandler 须要运行,循环并等待
mBlocked = true; // 设置阻塞状态为 true
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 运行 IdleHandler,只有第一次循环时才会运行
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 释放 IdleHandler 的引用
boolean keep = false;
try {
keep = idler.queueIdle(); // 执行 IdleHandler 的方法
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler); // 移除 IdleHandler
}
}
}
// 重置 IdleHandler 的数量为 0,确保不会重复运行
// pendingIdleHandlerCount 置为 0 后,上面能够经过 pendingIdleHandlerCount < 0 判断是不是第一次循环,不是第一次循环则 pendingIdleHandlerCount 的值不会变,始终为 0。
pendingIdleHandlerCount = 0;
// 在执行 IdleHandler 后,可能有新的消息插入或消息队列中的消息到了触发时间,因此将 nextPollTimeoutMillis 置为 0,表示不须要阻塞,从新检查消息队列。
nextPollTimeoutMillis = 0;
}
}复制代码
nativePollOnce(ptr, nextPollTimeoutMillis) 是调用 native 层的方法执行阻塞操做,其中 nextPollTimeoutMillis 表示阻塞超时时长:
nextPollTimeoutMillis = 0 则不阻塞
nextPollTimeoutMillis = -1 则一直阻塞,除非消息队列被唤醒
消息机制的流程以下:
准备阶段:
在子线程调用 Looper.prepare() 方法或 在主线程调用 Lopper.prepareMainLooper() 方法建立当前线程的 Looper 对象(主线程中这一步由 Android 系统在应用启动时完成)
在建立 Looper 对象时会建立一个消息队列 MessageQueue
Looper 经过 loop() 方法获取到当前线程的 Looper 并启动循环,从 MessageQueue 不断提取 Message,若 MessageQueue 没有消息,处于阻塞状态
发送消息
使用当前线程建立的 Handler 在其它线程经过 sendMessage() 发送 Message 到 MessageQueue
MessageQueue 插入新 Message 并唤醒阻塞
获取消息
从新检查 MessageQueue 获取新插入的 Message
Looper 获取到 Message 后,经过 Message 的 target 即 Handler 调用 dispatchMessage(Message msg) 方法分发提取到的 Message,而后回收 Message 并继续循环获取下一个 Message
Handler 使用 handlerMessage(Message msg) 方法处理 Message
阻塞等待