版权声明:本文出自汪磊的博客,转载请务必注明出处。

一、概述

我们知道更新UI操作我们需要在UI线程中操作,如果在子线程中更新UI会发生异常可能导致崩溃,但是在UI线程中进行耗时操作又会导致ANR,这时异步消息处理机制就登场了,大体流程就是我们在UI线程创建一个Handler,子线程创建一个Message,利用Handler将Message发送到MessageQueue中,然后轮到Looper登场了,Looper负责从MessageQueue中不断获取Message,交给Handler处理,最后在Handler的handleMessage方法中处理相应操作即可。

好了,大体说了一下异步消息处理机制的流程,Demo就不举例了,直接分析源码。

二、源码层分析

Looper源码分析

Looper中最重要的就是prepare()以及loop()方法,首先看下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));
}

prepare()方法调用prepare(boolean quitAllowed)方法,第6-8行首先检测当前线程中是否已经存储了Looper,如果已经存储过则抛出异常,这里我们就应该知道一个线程只能有一个Looper实例存在。sThreadLocal就是ThreadLocal,关于ThreadLocal我之前写过一篇介绍文章,这里就不详细说明了,请参照Android 异步消息处理机制前篇(一):深入理解ThreadLocal

第9行,如果当前线程没有存储过Looper,则new一个存储在当前线程。我们再看下Looper初始化的时候都做了什么:

 private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

主要就是初始化了一个MessageQueue赋值给mQueue变量,这里要明白,Looper初始化的时候会初始化一个MessageQueue与当前Looper绑定,后面会多次提到。

以上便是prepare方法的主要逻辑了,没什么复杂的,我们继续看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.");
}
final MessageQueue queue = me.mQueue; // 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(); for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
} // This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
} msg.target.dispatchMessage(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();
}
}

第2行,调用myLooper()。源码如下:

 public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

很简单就是获取当前线程存储的Looper实例,3-5行检测是否为空,为空的话则会抛出异常,这里就提示我们loop()方法一定要放在prepare()方法之后调用,否则会抛出异常。

第6行取出与当前Looper绑定的MessageQueue。接下来就进入13-45行的死循环了。

14-18行,从MessageQueue消息队列中获取message,如果没有则阻塞。

如果消息队列中存在未处理的message,则调用 msg.target.dispatchMessage(msg),target是什么鬼?其实就是Handler,这里先知道就可以了,后续会分析到。

44行,用完消息后对消息进行回收放进缓存池中,Message消息缓存池的实现原理请参照我之前的文章Android 异步消息处理机制前篇(二):深入理解Message消息池

loop()方法其实就是不断循环检查消息队列中是否存在未处理的消息,如果存在则交给Handler来处理。

Looper总结:

①Looper会在当前线程创建一个Looper实例存储在当前线程,并且会绑定一个MessageQueue。

②Looper的loop()方法,不断从MessageQueue中取消息,交给Handler的dispatchMessage去处理。

Handler源码分析

接下来我们分析Handler主要逻辑。先从Handler的创建开始,我们创建的时候一般都是调用空参数的构造函数:

 public Handler() {
this(null, false);
} 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;
}

本质上调用两个参数的构造函数。

15行,Looper.myLooper(),这里还记得吧,从当前线程获取存储的Looper实例。

16-19行,判断是否为空,为空则抛出异常。

20行,获取与当前线程中Looper实例绑定的消息队列,赋值给当前Handler中mQueue变量。

通过以上逻辑,Handler就与当前线程中的Looper,MessageQueue建立上了关联。

接下来Handler最重要的功能就是发送消息了,最常用的是sendMessage(Message msg),源码如下:

 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);
}

调来调去最终调用到sendMessageAtTime(Message msg, long uptimeMillis)方法。

13行获取MessageQueue ,14-19判断是否为空,如果不为空则执行20行逻辑,接下来我们看下enqueueMessage:

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

第2行,将当前Handler赋值给msg的targe,这里msg就会记住是哪个Handler发送的自己,在Looper的loop()方法中从消息队列中获取msg的时候,会调用 msg.target.dispatchMessage(msg)方法,交给发送自己的Handler的handleMessage方法来处理,这里说起来真绕口,不过这里一定要明白是怎么回事,面试中很可能会问到,比如,UI线程中创建Handler1,Handler2,Handler1发送msg1,Handler2发送Msg2,Handler2中的handleMessage是否会收到msg1?为什么?傻子都会回答不会,但是原理你能清清楚楚的讲解清楚吗,答案就是这里。

第6行,调用enqueueMessage方法将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);
}
}

第2行判断msg的callbac是否为空,如果不为空则调用 handleCallback方法:

     private static void handleCallback(Message message) {
message.callback.run();
}

很简单,就是调用run方法,首先这里就有两个小问题,1,run方法是否在子线程执行?2,平时我们发送msg也不给msg设置callbac啊,这里什么时候会用到?

回答问题1之前我们先看问题2,Handler的post方法大家应该都用过,典型用法如下:

 public class MainActivity extends Activity {  

         private Handler handler;  

         @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
//UI操作
}
});
}
}).start();
}
}

这样我们就可以在run方法里面进行UI操作了,显然这里run方法肯定是在主线程执行的,为什么呢?还是看下源码吧:

     public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

第7行就将我们传入的Runnable赋值给了msg的callback,然后执行发送消息到消息队列逻辑,之后就是Looper负责取消息发送给Handler,会调用dispatchMessage方法,上面讲过首先会对msg的callback进行判断是否为null,如果不为null,则直接调用run方法,到这里问题2应该就解决了,就是我们调用post方法设置的Runnable其本质是设置给了msg。

那么问题1呢?什么时候会在子线程执行,答案就是与Handler的创建所在线程是一致的,只不过我们大部分都是用来在主线程创建,子线程的情况用的比较少。

好了,我们回到Handler中dispatchMessage(Message msg)方法继续分析:

5-9行如果mCallback不为null则调用mCallback的handleMessage方法,mCallback的赋值是在Handler创建的时候。

如果mCallback为null则就调用Handler中的handleMessage方法:

     /**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

就是一个空方法,注释已经说了子类必须重写此方法接受消息进行处理。

好了到此Handler中核心部分就分析完了,小小的总结一下:

1、Handler的构造方法,会得到当前线程中保存的Looper实例,以及Looper实例中的MessageQueue,三者建立起联系。

2、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。

MessageQueue源码分析

MessageQueue就是一个容器,用来存放message,主要就是存入,取出message的操作。

首先存入源码如下:

 boolean enqueueMessage(Message msg, long when) {
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) {
// 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;
}

MessageQueue内部维护了一个链表,mMessages用于记录链表头部元素。

19-45行逻辑可以看出,对于messge会有一个按照时间排序的操作,这个也不难理解,在我们发送消息的时候可以指定发送的时候,这个时间并不是推迟入队的时间,而是给message打上时间标记,MessageQueue对message的维护会按照时间来排序。

其实这里和之前文章讲过的Android 异步消息处理机制前篇(二):深入理解Message消息池 的实现很像都是用链表实现的,只不过这里有个按照时间排序的操作。

取出的源码就不仔细分析了,大体过程说一下,在入队的时候会对message进行排序,并且mMessages会记录链表头部元素,取出message就是取出mMessages指向的消息,并且mMessages指向下一个消息,如果MessageQueue中不存在message,则阻塞一直等到有消息。

好了,讲到这里,异步消息通知最核心的部分就讲解完了。

异步消息通知总体流程总结:

老规矩,没有什么是一张图不能解决的

大部分关键点都在图中标注出来了,好了,本文到此就该结束了。

建议大家有时间看下View的post()方法以及 Activity的runOnUiThread()方法,其本质也都是使用异步消息通知这块技术点。

Android 异步消息处理机制终结篇 :深入理解 Looper、Handler、Message、MessageQueue四者关系的更多相关文章

  1. 【转】Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Andr ...

  2. Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    转自:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Android中的 ...

  3. android的消息处理机制(图文+源码分析)—Looper/Handler/Message[转]

    from:http://www.jb51.net/article/33514.htm 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.andro ...

  4. 【转载】Android异步消息处理机制详解及源码分析

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

  5. Android异步消息处理机制(多线程)

    当我们需要执行一些耗时操作,比如说发起一条网络请求时,考虑到网速等其他原因,服务器未必会立刻响应我们的请求,如果不将这类操作放在子线程里去执行,就会导致主线程被阻塞住,从而影响用户对软件的正常使用. ...

  6. Android 异步消息处理机制 让你在深入了解 Looper、Handler、Message之间的关系

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 非常多人面试肯定都被问到过,请问And ...

  7. Android异步消息处理机制

    安卓子线程无法直接更改UI,所以需要异步消息处理机制来解决 <?xml version="1.0" encoding="utf-8"?><Li ...

  8. Android 异步消息处理机制解析

    Android 中的异步消息处理主要由四个部分组成,Message.Handler.MessageQueue.Looper.下面将会对这四个部分进行一下简要的介绍. 1. Message: Messa ...

  9. Android异步消息处理机制完全解析,带你从源码的角度彻底理解(转)

    开始进入正题,我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一 ...

随机推荐

  1. Ajax.Nodejs.跨域访问

    使用环境: 客户端: jQuery 服务器: Node.js 在通过Ajax调用非本域的链接/接口时, 一般是不能成功的, 就算是同一个IP下不同的端口也被认作跨域访问 解决办法记录如下: 客户端: ...

  2. eclipse项目中引入shiro-freemarker-tags会jar包冲突

    maven项目中引入了这个依赖. <dependency> <groupId>net.mingsoft</groupId> <artifactId>sh ...

  3. Elasticsearch 索引别名与Template

    在使用elasticsearch的时候,经常会遇到需要淘汰掉历史数据的场景. 为了方便数据淘汰,并使得数据管理更加灵活,我们经常会以时间为粒度建立索引,例如: 每个月建立一个索引:monthly-20 ...

  4. 从json_encode过来的的字符串被返回到html页面时的解析

    在工作过程中经常需要向服务器请求数据.在需要返回多个值的时候,使用json_encode处理数组然后返回是很常用的做法.如果没有指定返回数据类型的情况下,默认返回的是json格式的字符串.那么需要将这 ...

  5. Flex 布局实例

    如图: 代码如下: <!DOCTYPE HTML> <html> <meta charset="utf-8"> <head> < ...

  6. OpenCV畸变校正源代码分析

    图像算法中会经常用到摄像机的畸变校正,有必要总结分析OpenCV中畸变校正方法,其中包过普通针孔相机模型和鱼眼相机模型fisheye两种畸变校正方法. 普通相机模型畸变校正函数针对OpenCV中的cv ...

  7. Linux网络中接收 "二进制" 流的那些事 --- 就recv的返回值和strlen库函数进行对话

    1.    前言 很多朋友在做网络编程开发的时候可能都遇到这样的问题,在进行接收二进制流的数据的时候,使用strlen库函数来得到 二进制数据长度的时候并不准确.为什么呢??首先,使用strlen进行 ...

  8. Attribute在.NET编程中的应用(三)

    用于参数的Attribute 在编写多层应用程序的时候,你是否为每次要写大量类似的数据访问代码而感到枯燥无味?比如我们需要编写调用存储过程的代码,或者编写T_SQL代码,这些代码往往需要传递各种参数, ...

  9. c#读取并分析sql Server2005数据库日志

    用过logExplorer的朋友都会被他强悍的功能吸引,我写过一篇详细的操作文档可以参考http://blog.csdn.net/jinjazz/archive/2008/05/19/2459692. ...

  10. 【python】内置函数总结(一)

    1.判断真假的函数:bool()2.Python中所谓的迭代协议就是next方法的对象会前进到下一个结果,在一系列结果的末尾会引发StopIteration异常.在Python中,任何类型的对象都被认 ...