异步消息处理机制之Handler源码分析篇
来源:互联网 发布:linux开启ftp服务 编辑:程序博客网 时间:2024/06/05 15:39
异步消息处理机制之Handler源码分析
Android中的异步消息处理机制主要由四个部分组成, Message、Handler、MessageQueue和Looper。
1、Message
Message是在线程之间传递的消息,可以携带少量消息。可在不同线程之间交换数据
2、Handler
Handler是处理者的意思,主要的功能用于发送消息和处理消息。发送消息一般使用sendMessage()方法,发出的消息经过处理,最终传到Handler的handlerMessage()方法中处理
3、MessageQueue
MessageQueue是消息队列的意思,主要用于存放经由Handler发送的消息。这写消息将会一直存到消息队列中,等待被处理且每一线程只有一个MessageQueue对象
4、Looper
Looper是每个线程的MessageQueue管家,通过调用Looper()的loop()方法后,就会进入一个无限循环中,如果MessagQueue中有消息就取出,并传递到Handler的handleMessage方法中。每个线程只有一个Looper对象。
将前面的概念进行总结
首先在主线程中创建一个Handler对象,重写handleMessage()方法,然后当子线程需进行UI操作时,就会创建一个Message对象,然后通过Handler的sendMessage()将消息发送出去,这条消息最终将被添加到MessageQueue队列中等待被处理,而Looper将会不断尝试从MessageQueue中取出消息并处理消息,最后分发回Handler的handleMessage()方法中。由于Handler是在主线程创建,那么handleMessage也会在主线程中执行,这样就可以进行UI操作了。
接下来就是进行源码分析了…….
需求? 子线程发送一串字符串到主线程,通过吐司的方式显示(省略了一些代码…请读者自己实现下吧! 代码简单到不好意思!!!)
// 这段代码放在主线程中private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { // UI thread switch (msg.what) { case 1: Toast.makeText(MainActivity.this, "" + msg.obj, Toast.LENGTH_SHORT).show(); break; default: break; } };};//这段代码在子线程中 new Thread(new Runnable() { @Override public void run() { Message msg = new Message(); msg.what = SUCCESS; msg.obj = "我正做着不可描述的事情..."; mHandler.sendMessage(msg); } }).start();
疑问一 怎样获取消息的呢?
点击源码进入Message类中可看到obtain()静态方法
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { //判断消息是否为空,不为空则取出头节点,这里是用单链表管理(数据结构的知识哦!!!) Message m = sPool; //获取头节点 sPool = m.next; //头结点指向下一个消息 m.next = null;//将当前取出的消息的next指针域置为空 sPoolSize--; //消息数量减一 return m;//返回取出的节点 } } return new Message(); //如果消息队列中没有消息,则直接创建一个新的消息 }
疑问二 怎样发送消息的呢?
点击sendMessage()进入,然后一直跟下去,最终调用的是sendMessageAtTime(Message msg, long uptimeMillis)方法。第一个参数发送的消息,第二个参数判断是不是及时消息(在后续分析中很有用,暂且不管)
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue;//获取消息队列 .... //省略了一部分代码,关于异常处理的 return enqueueMessage(queue, msg, uptimeMillis); //我们又发现调用了这个方法,接着跟下去,代码如下 }private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; //这里实际上拿到了的是当前Handler对象 ...//省略代码 return queue.enqueueMessage(msg, uptimeMillis); //我们可以看到这里调用的是MessageQueue的enqueueMessage,接着往下走,代码如下: }
//哇!!! 一大波代码来袭,这里我挑重点介绍
boolean enqueueMessage(Message msg, long when) { ......//省略代码部分 msg.when = when; //当前消息的时间,即在队列中多久后发出这个消息 Message p = mMessages;//获取消息队列头结点 if (p == null || when == 0 || when < p.when) {//判断是否是及时消息,也就是说是不是需要立马发出去的消息 msg.next = p;//待发出去的消息的next指针域指向头结点 mMessages = msg;//头结点指向待发出去的消息节点,相当于头插 needWake = mBlocked; } else { // 那如果不是及时消息呢? 如果when 不等于0, 那么是不是会从消息队列中找合适可以插入的位置呢? needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //保存上一个节点 for (;;) { //循环遍历消息队列,实际上就是由排序好的单链表管理 prev = p; //始终保存当前节点的上一个节点位置 p = p.next;//不断的前进 if (p == null || when < p.when) {//找到合适的位置,比如 插入的消息when = 3,而当前排序好的队列中有为when=4的消息, 那么就将该消息插入when = 4的前面, break跳出的循环就是建立链表的逻辑 break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // 待插入的消息的next指针域指向当前遍历退出的节点 prev.next = msg;//由之前定义的pre节点的指针域指向待插入的节点,这样就把它链接起来啦 } } return true; }
到这里已将怎样获取消息、怎样发送消息讲解完毕。下面是不是应该分析Looper类的功能了呢?Looper是怎样从MessageQueue中取出消息? 又是怎样分发到Handler的handleMessage中进行处理的呢? 继续往下看!!!
点击Looper类进去查看它做的是什么功能?我们看它的官方描述
Class used to run a message loop for a thread. Threads by default do * not have a message loop associated with them; to create one, call * {@link #prepare} in the thread that is to run the loop, and then * {@link #loop} to have it process messages until the loop is stopped. * * <p>Most interaction with a message loop is through the * {@link Handler} class. * * <p>This is a typical example of the implementation of a Looper thread, * using the separation of {@link #prepare} and {@link #loop} to create an * initial Handler to communicate with the Looper. * * /
我将这段话总结如下: Looper类有个loop方法可以从无限循环的消息队列中获取消息, 官方给了一个例子如下:
class LooperThread extends Thread { //创建子线程 * public Handler mHandler; * * public void run() { * Looper.prepare(); //这个方法是什么呢? 待会儿往下看....... * * mHandler = new Handler() { //很熟悉吧!我们在主线程中也创建过这个类吧! * public void handleMessage(Message msg) {//这个方法就是将取出的消息分发到这儿来处理 * // process incoming messages here * //要处理的消息放在这儿吧!!!! * } * }; * * Looper.loop(); //我们大概说过,loop()方法是无限循环获取消息吧 * }
官方代码是不是跟我们的有一些区别呢? 我们是在主线程中通过new Handler()处理的消息吧!那这里又有什么区别呢?首先我们需要明确一点,每一线程都有自己的Looper对象, 那么主线程肯定也有自己的Looper对象吧! 继续看源码。查看Looper类中有一个方法prepareMainLooper(),代码如下:
/** 这段注释尤其重要,它讲述的是:主线程的Looper对象是由Android系统启动而调用的,是不是很复杂呢? 接下来就需要查看系统源码了,先大概抛出了一个类,ActivityThread.java,等会讲解。 * 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); //下面讲解。。。。。 ......//省略代码 sMainLooper = myLooper(); //获取主线程Looper,这里还得继续看源码 }}//返回一个sThreadLocal.get(),这又是什么呢?接着往下看public static Looper myLooper() { return sThreadLocal.get(); } static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); //是不是有点像一个集合呢?根据泛型得知,存储的是Looper对象吧! 其实这就是一个本地线程管理,用来区分当前是哪个线程的Looper对象。 这里是不是应该有疑问? 为什么没有看到怎样存Looper对象的呢?如果你没有存储, 你又是怎样取出来的呢? 我们刚才有个方法没有讲吧! prepare(false);我们看它的实现方法,代码如下: private static void prepare(boolean quitAllowed) { .....//省略代码部分 sThreadLocal.set(new Looper(quitAllowed)); //看到这儿,是不是一切都明了啦, 相当于说这里将当前线程的Looper的对象添加到本地线程管理, 然后通过get方法取出来呢?}
哇!!! 相信看到这人有些哥们已经下车了吧!!!多理解理解吧!! 接下来查看之前抛出来的ActivityThread.java类了。源码如下:
public static void main(String[] args) { SamplingProfilerIntegration.start(); ....//省略部分代码 Looper.prepareMainLooper(); //是不是看到了呢? 实际上就是当Android系统启动分为两部分,一部分是底层驱动启动, 另外一部分是Android上层应用启动,在这里说明了,主线程的Looper是由系统启动的。 if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); thread.attach(false); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
接下来就是最后一个方法了, Looper类的loop方法到底做了些什么? 不废话,直接上源码:
//看到这么多代码,是不是很燥呢? 接下来我进行细致分析。 public static void loop() { final Looper me = myLooper(); //获取当前的Looper对象 ...//省略部分代码 final MessageQueue queue = me.mQueue;//获取当前消息队列 ...//省略部分代码 for (;;) { //重头戏, 这不就是前面所说的死循环从消息队列中获取消息吗? Message msg = queue.next(); // 获取消息 ...//省略部分代码 msg.target.dispatchMessage(msg); //终于看到了消息的分发了, 通过Handler的dispatchMessage()将消息分发出去,等会做细致讲解。 ...//省略部分代码 msg.recycle(); //清空消息 } }/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { //回调handleMessage()这个方法。 return; } } handleMessage(msg); } }
强调一下: 由于主线程的Looper的loop()是在主线程中执行,那么必然有分发消息也是在主线程中执行,再推理,也就是说,最终handleMessage()就是在主线程中执行了!!! 那么就可以进行UI操作了
总结
在主线程中创建了Handler对象,并覆写了handleMessage()方法, 而我们在子线程中创建消息(当然也可以从消息队列中获取复用,我们这里的消息队列数为10个,我就不做讲解了。最终将其发出去),最终被添加到MessageQueue()队列中(按条件插入合适的位置),然后交由当前Looper的loop()方法从MessageQueue()中取出消息。最终分发到handleMessage()中执行
示意图如下
0 0
- 异步消息处理机制之Handler源码分析篇
- Looper、Message、MessageQueue、Handler异步消息处理机制源码分析
- Android异步消息处理机制详解及源码分析 Handler
- Handler异步消息处理机制的源码分析
- 异步消息处理机制Handler源码解析
- Handler消息处理机制源码分析
- Handler消息处理机制---从源码分析
- 源码分析 -- 异步消息处理机制
- 源码分析异步消息处理线程机制(Looper MessageQueue Handler Message)
- Android中的Looper , Handler , Message的关系,异步消息处理的机制,根据源码分析
- Android源码分析之Handler消息机制
- Handler--异步消息处理机制
- 异步消息处理机制(Handler 、 Looper 、MessageQueue)源码解析
- Android异步消息处理机制之handler机制
- Android之异步处理Handler和消息机制处理
- 26、Android之Handler异步消息处理机制
- Android之多线程----异步消息处理机制之Handler详解
- Android多线程----异步消息处理机制之Handler
- Linux进程的虚拟存储器知识点
- android 反编译获取代码
- Java语句轻松实现与数据库MYSQL【本地数据库】的连接,和对数据库的增删改查操作
- 8.1 Zend_View
- 我为什么写技术博客
- 异步消息处理机制之Handler源码分析篇
- js正则表达式小记
- Myeclipse2014搭建Maven
- C++命名注意
- noj1748:约瑟夫问题
- Mac appium 自动化环境搭建
- 浅谈 磁盘调度算法
- GC
- HTML5视频