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

一、概述

我们知道更新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. js正则验证特殊字符

    js正则验证特殊字符 方案一 var regEn = /[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/im, regCn = /[·!#¥(--):: ...

  2. 我推荐的 Java Web 学习路线

    晚上再 V2 的 Java 的节点看到有人问 Java Web 书籍推荐.我这半年多的时间,也从别的方向开始转向 Java 服务端开发,所以,我来说下我的学习路线,帮助有需要的朋友把半只脚踏进 Spr ...

  3. LeetCode 119. Pascal's Triangle II (杨辉三角之二)

    Given an index k, return the kth row of the Pascal's triangle. For example, given k = 3,Return [1,3, ...

  4. Ceph: A Scalable, High-Performance Distributed File System译文

    原文地址:陈晓csdn博客 http://blog.csdn.net/juvxiao/article/details/39495037 论文概况 论文名称:Ceph: A Scalable, High ...

  5. 【G彩娱乐网】作为一名程序员,我应该如何选购一台电脑?

    G彩娱乐网说到程序员专用电脑,那肯定是苹果电脑.优点有很多,比如白平衡特别准.酷炫的黑科技.特别方便的软件等显而易见的优势:也有能够增加提案通过率.专注工作提高工作效率这样的玄学buff. 但是!并不 ...

  6. ⑾bootstrap组件 徽章 大屏 页头 基础案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. ruby 安装 mysql2 命令

    sudo apt-get install libmysql-ruby libmyclient-dev

  8. 爬虫入门 手写一个Java爬虫

    本文内容 涞源于  罗刚 老师的 书籍 << 自己动手写网络爬虫一书 >> ; 本文将介绍 1: 网络爬虫的是做什么的?  2: 手动写一个简单的网络爬虫; 1: 网络爬虫是做 ...

  9. Oracle单行函数基础运用

    单行函数 整个SQL的精髓:select语句+单行函数(背) 字符串函数 常用的处理字符串的函数有如下: No. 函数名 含义 1 UPPER(c1)  upper 将字符串全部转为大写 2 LOWE ...

  10. java的String构造对象的几种方法以及内存运行过程

    String类创建对象的方法可以分为以下三种 1.String a = "123"; 2.String b = new String("123"); 3.Str ...