前言:由于Android系统本身决定了其自身的单线程模型结构。在日常的开发过程中,我们又不能把所有的工作都交给主线程去处理(会造成UI卡顿现象)。因此,适当的创建子线程去处理一些耗时任务是非常关键的。同时Android中非UI线程不能对UI组件进行操作,因此,熟练的掌握并应用线程间消息通信是很有必要的。接下来,我们从Android线程间通信机制和Android消息机制两个方面对以上内容进行介绍。
一.Android线程间通信机制
Android的线程间通信主要是在非UI线程对UI线程的消息传递,并且修改UI界面的操作。Android中常见的子线程更新UI的方式:
接下来我们依次看一下这几种调用方法的内部实现原理。
– handler.post(Runnable r)内部实现原理:
//Handler.post()方法:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
//Handler.sendMessageDelayed()方法:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis <0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//Handler.sendMessageAtTime()方法:
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);
}
调用链:Handler.post->sendMessageDelayed->sendMessageAtTime
从上面的调用链可以看出,Handler.post方法最终调用了Handler.sendMessageAtTime方法,而通过sendMessageAtTime的实现逻辑可以看出,最终还是通过enqueueMessage方法将Message插入到消息队列,进行轮询处理。因此,底层还是Handler消息机制。
&#8211; runOnUiThread(Runnable r)内部实现原理:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
runOnUiThread内部实现机制到这里基本就可以结束了,因为它的实现原理一目了然。简单的判断当前线程是否是UI线程,如果是,就直接执行Runnable方法。如果不是,就通过Handler.post方法处理这个Runnable。因此,底层还是Handler消息机制。。
&#8211; view.post(Runnable r)内部实现原理:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
和runOnUiThread内部实现相似,view.post方法也是通过Handler.post方法处理这个Runnable。
从上面的分析可以看出,所有的自线程更新UI组件的方式都是依靠Handler消息机制实现的。因此,接下来我们将详细介绍Android的消息机制-Handler消息机制。
二.Android消息机制(Handler消息机制)
Handler消息机制的运行需要底层的MessageQueue和Looper来支撑,它的主要作用是将一个任务放到某个指定的线程中去执行。接下来我们详细介绍Handler、Message、MessageQueue、Looper的作用和联合工作机制。
MessageQueue:中文意思是消息队列。顾名思义,它的内部存储了一组消息,以队列的形式对外提供添加和删除操作。可能很多小伙伴从它的字面意思,就认为它就是一个队列的数据结构。其实并不是,它的内部是采用单链表的数据结构来存储消息列表,最初的存在形式就是一个空的头节点(mMessage)。
Message:中文含义为消息。很明显,它就是我们上面说到的MessageQueue中存储的实体。上面说到,MessageQueue是以单向链表的形式存储数据,每一个Message都是其中的一个节点。因此开始时,队列为空,即没有需要处理的消息。当有消息到来时,就添加节点到队列尾部(添加新节点到链表尾部,记录Next域)。
Message数据结构:
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
//记录下一个节点域
android.os.Message next;
}
上面的Message结构只是简单罗列了一些我们常用的字段,其中实现链表最关键的字段就是next字段,它是实现消息队列的核心。我们创建Message对象有多种方式,但是建议通过Message的obtain方法去获取Message对象,而不是直接创建新的Message对象。想了解更多关于obtain方法内部实现原理,请移步:Handler消息机制之深入理解Message.obtain()
Looper:中文含义为循环,在这里即为消息循环。由于MessageQueue只是一个消息的存储单元,它不能去处理消息,而Looper正好弥补了这个缺陷。Looper会以无限循环的方式去查找是否有新的消息,如果有消息,就取出来处理,否则就一直等待。除此之外,它还有一个特殊的概念:ThreadLocal(存储和获取当前线程的Looper),在这里不作过多阐述,想要了解ThreadLocal是如何实现线程私有化存储机制的,可以移步 ThreadLocal内部实现机制详解 查看详情。
Handler:主要功能是切换到指定线程去处理Looper轮询查找到的新消息。
Looper工作原理:
Looper是消息机制中的消息循环角色,它会不断轮询查找MessageQueue中是否有新的消息需要处理。一旦发现需要处理的新消息,就立刻取出消息交给Handler进行处理;否则就一直阻塞在那里。
我们都知道,Looper、Thread、MessageQueue一定是绑定在一起的,因为他的作用就是轮询处理当前线程的消息队列,从它的构造器就可以看出:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper类常用方法:
//判断当前线程是否有Looper,没有就创建一个Looper,并且放到当前线程的ThreadLocal中。
//ThreadLocal:每个线程都有,并且是线程私有的,用于存储线程私有数据
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));
}
Looper.prepareMainLooper()方法:和prepare方法类似,只是它专用于给主线程创建Looper。
Looper.loop()方法:用于消息轮询,以下是loop方法部分重要代码解析。
public static void loop() {
//获取线程的Looper对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取Looper需要轮训的消息队列
final MessageQueue queue = me.mQueue;
//开启无限的消息轮询(死循环),唯一跳出循环的方法就是下面msg为空,即MessageQueue调用quit方法退出 Looper。当消息队列被标记为退出状态时,next会返回null。
for (;;) {
//取出需要处理的消息,next是一个阻塞方法,当获取不到要处理的消息时,就会阻塞在这里。当消息队列标记为退出状态是时,不会阻塞,会返回null。接下来,判断到msg为null,就会退出循环。
Message msg = queue.next(); // might block
//如果上面next方法没有阻塞,msg就不可能为空。只有在Looper退出后(调用quit方法),msg才会为空。
if (msg == null) {
return;
}
try {
//msg.target其实是一个Handler对象,这句代码就是处理消息的关键代码,将Message交给Handler去处理
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//参数复位操作
msg.recycleUnchecked();
}
}
Handler工作原理:
简介:Handler的主要任务是发送一条消息和获取并处理一条消息。接下来我们总结一下Handler发送消息和处理消息的一些常用方法,并且分析其实现原理。
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
在sendMessage内部调用sendMessageDelayed方法:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis <0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
在sendMessageDelayed内部调用sendMessageAtTime方法:
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);
}
通过sendMessageAtTime方法逻辑可以看出,最终还是调用enqueueMessage方法将Message插入到消息队列。接下来的逻辑就不用多说了吧。Looper轮询就该上场了…
Handler调用链:sendMessage->sendMessageDelayed->sendMessageAtTime->Looper轮询Handler处理消息。
Handler.post(Runnable r)方法:post方法上面已经详细分析了,调用的也是sendMessageDelayed方法去处理Runnable中的Message。
上面是两个重要的发送消息的方法,接下来看一下当Looper轮询检测到新消息时的处理方法。上面提到,当Looper调用loop方法轮询的时候,检测到新消息会有这么一行代码:msg.target.dispatchMessage(msg)
,这就是消息分发处理的方法,下面重点讲解这个方法。
Handler.dispatchMessage(Message msg)方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
其中的msg.callback实在创建Message的时候声明的,声明方式如下:
//在创建Message的时候匿名创建的runable对象,一般用不到
Message message = Message.obtain(handler, new Runnable() {
@Override
public void run() {
}
});
//直接调用Runable的run方法,处理逻辑。即调用上面创建Message时自己实现的run方法
private static void handleCallback(Message message) {
message.callback.run();
}
msg.callback为空的情况:检测mCallback是否为null。不为null,就调用mCallback的handleMessage方法处理这条消息;当mCallback为null的时候,直接调用Handler中的handleMessage方法处理消息。其实Handler中的handleMessage方法是一个空实现的方法体,也就是说当mCallback也为null的时候,就不对这条消息进行处理了。接下来再说略显复杂的mCallback不为空的情况。
mCallback是一个接口,看接口里面的声明方法public boolean handleMessage(Message msg)
,这不就是我们创建Handler的时候处理消息的方法嘛。别急,看一下在哪里给mCallback赋值的。没错,就是在Handler的构造器里面对它进行的赋值操作。因此,mCallback也是我们在创建Handler的时候自己定义的,处理消息的逻辑也就是我们自定义实现的。
//这就是我们创建Handler对象时常用方法,这里匿名创建的Callback就是上面提到的Handler中的mCallback。
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//处理Message
return false;
}
});
总结:Handler发送消息和处理消息的方法都介绍完了,想必大家都很清楚其中的实现思路了。最后,我们总结一下整个Handler消息处理机制的流程:
结语:希望通过本文的分析,让小伙伴们对Handler消息处理机制能有更深刻的理解。如有不对的地方,望大家指出;如果对您有帮助,望多多支持。