Handler异步消息处理机制的源码分析
2016-09-21 20:36
567 查看
1. Handler异步消息处理机制
主线程不能进行耗时操作 (会发生ANR异常)子线程不能更新UI的操作 (因为会报异常——>只有产生该控件的线程才能操作该控件。)
那么 , 要在子线程请求数据,然后将数据发送到主线程中更新UI , 此时需要使用Handler机制.
2. Handler的核心组成部分
Handler : 用于发送消息和处理消息Message : 用于携带数据和通知
MessageQueue : 以单链表的形式用于存储消息
Looper : 死循环,如果消息队列里有消息就将其取出然后交给对应的Handler处理,如果没有就处于等待状态
3. Handler的异步消息处理流程图
首先需要在主线程中创建一个Handler对象 , 并重写handleMessage()方法 , 然后当子线程中需要进行UI操作时 , 就创建一个Message对象 , 并通过Handler将这条4000
信息发送出去 , 之后这条信息会被添加到MessageQueue的队列中等待被处理 , 而Looper则会一直尝试从MessageQueue中取出待处理消息 , 最后分发会Handler的handlerMessage()方法中.由于Handler是在主线程中创建的 , 所以此时handlerMessage()方法中的代码也会在主线程中运行 , 进行UI操作.
异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。
4. Handler的简单使用方法
在主线程中创建一个Handler对象 , 重写里面的方法// 创建一个Handler对象 private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // 处理消息 } };
在子线程中通过mHandler发送信息
// 创建一个Message对象 Message msg = Message.obtain(); // 将消息存入Message对象中 msg.obj = null; // 给消息添加标记 , 例如 SUCCESS 和 FAILED msg.what = 0; // 通过mHandler发送消息到主线程 mHandler.sendMessage(msg);
在主线程中进行操作
handle.post(new Runnable() { @Override public void run() { } });
5. Handler的源码分析
(1) 首先 , 我们来看看Message对象的创建 , 一共有3种方式Message msg = new Message(); Message msg = Message.obtain(); // 发送消息 handler.sendMessage(msg); // handler已经绑定 Message msg = handler.obtainMessage(); // 发送消息 msg.sendToTarget();
handler.obtainMessage()方法里底层实质上也是调用了Message的obtain(handler)方法 , 然后通过sendToTarget()发送出去
public final Message obtainMessage() { return Message.obtain(this); }
Message继承了Parcelable接口 , 可实现序列化 , 那么 , 接下来 , 我们看看Message对象里的obtain()方法 , 里面到底做了什么 :
public final class Message implements Parcelable
/*package*/ Message next; private static final Object sPoolSync = new Object(); private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; 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(); }
从中可以看到一个同步块 , 通过一个new的object对象进行加锁 , 然后 , 在同步块里 , 如果sPool不为null , 那么将sPool赋值给了m , 最后返回出来 , 如果sPool为null , 那么就重新new一个Message对象.
这时 , 大家一定奇怪了 , 第11行和第12行的这两行代码又代表着什么 , 其实 , 这里消息池是一个单链表结构 , 这两行代码意味着从消息池中取出Message对象后 , 对sPool的位置进行重置.
然后 , 我们再来看Message的obtain(Handler h)方法 :
public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; }
其中 , target就是对Handler进行标记 , 比如一个应用中创建了多个Handler对象来发送消息 , 那么就可以用target来进行区分.
好 , 我们接下来再看一下Message里的recycle()方法 , 对消息进行回收 , 里面的属性都设置为默认值 :
public void recycle() { ... recycleUnchecked(); } void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
(2) 我们再走进Handler对象中 , 切入主入口 , Handler的实例化
public Handler() { this(null, false); }
实质上是调用了它的重载方法 , 传入两个参数 , null 和 false , 如下 :
public Handler(Callback callback, boolean async) { // 此处省略一万行代码 ... // 轮询器,轮询取消息 mLooper = Looper.myLooper(); // 消息队列,存放handler发的消息 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
关键的部分来了 , 第6行和第9行代码 , 获取了轮询器的对象mLooper , 以及消息队列mQueue , 此处表示handler和mLooper共享同一个消息队列 . 那我们首先来看mLooper的初始化 , Looper.myLooper()方法 :
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); /** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
其中 , sThreadLocal是代表本地线程变量 , 每个线程都可以去调用它 , 它也知道是哪个线程在存放数据 , 那么这里的get()方法 , 就是去获取数据 , 这时 , 你一定奇怪它是什么时候存的呢?这里要提到一个ActivityThread类 , 代表主线程 , 在应用程序运行之前 , ActivityThread类就已经初始化了 , 我们来看里面的核心代码 :
//主线程 public static final void main(String[] args) { 。。。 //轮询器的初始化 Looper.prepareMainLooper(); 。。。 //轮询器开始取消息 Looper.loop(); }
其中 , prepareMainLooper()里面实质是调用了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)); }
可以看到 , sThreadLocal.set()方法添加了一个新的Looper对象 , 也就是说 , 在创建Handler之前 , UI线程中就已经调用了Looper.prepare()和Looper.loop()方法. 在看Looper.loop()方法之前 , 我们先看一下Looper的构造方法 , 因为在主线程中已经创建了一个Looper对象
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
从上面的构造函数中可以看到 , Looper在初始化时 , 就默认创建了一个消息队列对象MessageQueue.
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
那我们再来看一下Looper.loop()方法 , 轮询取消息了 , 不过还没有发送消息 , 这时是取不到消息的
public static void loop() { // 当前是主线程,只有一个Looper对象,同handler一起共享 final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } // 与handler共享同一个消息队列 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; } ... msg.target.dispatchMessage(msg); ... msg.recycleUnchecked(); } }
代码比较多 , 其实核心代码就几个 , 其中Message msg = queue.next() , 从系统运行开始就一直无限循环取消息 , 所以当消息为null时 , 可能会造成阻塞. 那我们来看一下next()方法里是什么
Message next() { ... // 代码太多,但是看到这一行代码就足够了 nativePollOnce(ptr, nextPollTimeoutMillis); ... }
这里就涉及到JNI机制了 , 底层的通信交给本地代码了
private native static long nativeInit(); private native static void nativeDestroy(long ptr); private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/ private native static void nativeWake(long ptr); // 唤醒 private native static boolean nativeIsPolling(long ptr); private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
这里扩展一下 , inux进程间的通信,linux管道通讯,管道其实就是个特殊的文件,该文件有两个描述符(操作文件的句柄(引用)),读的描述符,写的描述符
通讯的原型
主线程拿着读的描述符等待读取数据,子线程拿着写的描述符开始写数据,写完数据之后通知拿着读描述符的主线程,主线程唤醒,开始轮询取消息,处理消息,如果没有消息中断(阻塞)
(3) 发送消息
调用handle.sendMessage(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); }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // handler发送消息,把当前的handler对象绑定到message对象中 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } // 把消息放到消息队列里,并排序 return queue.enqueueMessage(msg, uptimeMillis); }
感觉发消息发了好久 , 上面的sendMessageDelayed()方法可以延迟发消息 , 最终实质上是调用了queue.enqueueMessage(msg, uptimeMillis)方法 , 把消息放到消息队列里,并排序.
boolean enqueueMessage(Message msg, long when) { // this表示同一个容器,消息队列 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) { // 唤醒jni nativeWake(mPtr); } } return true; }
(4) 取消息
又回到上面的Looper.loop()方法了 , 精简版
public static void loop() { final Looper me = myLooper(); final MessageQueue queue = me.mQueue; for (;;) { // 取消息 Message msg = queue.next(); // might block if (msg == null) { return; } // 处理信息 msg.target.dispatchMessage(msg); ... msg.recycleUnchecked(); } }
走到这一步了 , 让我们来看看handler最后是如何处理消息的 , msg.target.dispatchMessage(msg) , 注意的是 , 该方法是在主线程中运行的 , 因为loop()方法就是在主线程中运行
public void dispatchMessage(Message msg) { // 发送信息时,默认msg.callback为null if (msg.callback != null) { // 处理runOnUiThread方法 handleCallback(msg); } else { // 我们走这里,msg.callback==null if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } // 主线程,方法回调 handleMessage(msg); } }
最后回调给了主线程中的handler的handleMessage()方法 , 这就是Handler异步消息处理机制的全过程.
6. runOnUiThread方法的源码分析
实质上也是调用了Handler的post方法public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { // 关键点 mHandler.post(action); } else { action.run(); } }
接下来我们深入看下Handler的post方法的代码逻辑
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
看到sendMessageDelayed()方法是不是有种很亲切的感觉 , 跟上面不同 , 此时传入的第一个参数msg为getPostMessage(r)
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
那么 , 重点来了 , 返回一个新的Message对象 , 只是多添加了一个callback属性 , callback不再为null , 走到最后的handler.dispatchMessage(msg)方法
public void dispatchMessage(Message msg) { // 此时,msg.callback不为null if (msg.callback != null) { // 处理runOnUiThread方法 handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
此时 , msg.callback不为null , 那么就走handleCallback(msg)方法
private static void handleCallback(Message message) { message.callback.run(); }
这里的message.callback就是传入的Runnable , 它本身跟线程是没有任何关系的 , 之所以会在主线程中运行 , 完全是因为dispatchMessage()方法是在主线程中运行的 , 然后调用Runnable.run()方法.
欢迎大家交流 , 共同探讨!
相关文章推荐
- Android中的Looper , Handler , Message的关系,异步消息处理的机制,根据源码分析
- Android异步消息处理机制详解及源码分析 Handler
- Looper、Message、MessageQueue、Handler异步消息处理机制源码分析
- 源码分析异步消息处理线程机制(Looper MessageQueue Handler Message)
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- (转)android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- [转] android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android应用程序消息处理机制(Looper、Handler)源码分析
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message