之前写过一篇文章,概述了Android应用程序消息处理机制。本文在此文基础上,在源码级别上展开进行概述

简单用例

Handler的使用方法如下所示:

Handler myHandler = new Handler() {  public void handleMessage(Message msg) {   switch (msg.what) {   ...  }}   };class myThread implements Runnable {   public void run() {  while (!Thread.currentThread().isInterrupted()) {    Message message = Message.obtain();   message.what = TestHandler.GUIUPDATEIDENTIFIER;TestHandler.this.myHandler.sendMessage(message);   message.recycle();try {   Thread.sleep(100);    } catch (InterruptedException e) {   Thread.currentThread().interrupt();   }   }   }   }

或者:

mHandler=new Handler();
mHandler.post(new Runnable(){void run(){...}
});

又或者:

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

源码解析

首先看其构造函数:

new Handler()
...
public Handler() {this(null, false);
}
...
public Handler(Looper looper, Callback callback) {this(looper, callback, false);
}
...
public Handler(Callback callback, boolean async) {if (FIND_POTENTIAL_LEAKS) { // 默认为false,若为true则会检测当前handler是否是静态类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());}}// 1. 获得当前线程的loopermLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}//2. 获得looper上的message queuemQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;
}

由此引入了两个关键对象Looper和MessageQueue。

先看 mLooper = Looper.myLooper(); 这一句发生了什么:

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

可以看到,该方法返回一个sThreadLocal对象中保存的Looper。关于ThreadLocal类,请参考这里,本文不展开。
如果尚未在当前线程上运行过Looper.prepare()的话,myLooper会返回null。接下来看看Looper.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));
}

可以看到该方法只是简单地新建了一个Looper对象,并将其保存在sThreadLocal中。接下来看一下Looper的构造函数。

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

调用完Looper.prepare()后,需调用Looper.loop()才能使消息循环运作起来,其源码如下所示:

public static void loop() {final Looper me = myLooper(); //1. 取出looper对象if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue; //2. 取出looper绑定的message queue// 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();// loop time.long tm = 0;...for (;;) {Message msg = queue.next(); // 3. 堵塞式在message queue中取数据if (msg == null) {// No message indicates that the message queue is quitting.return;}...msg.target.dispatchMessage(msg); 4. 分发message到指定的target handler...// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {...}msg.recycleUnchecked(); // 5. 回收message对象...}
}

可以简单地将Looper.loop()理解成一个不断检测message queue是否有数据,若有即取出并执行回调的死循环。 接下来看一下Message类:

public final class Message implements Parcelable {public int what;public int arg1;public int arg2;public Object obj;.../*package*/ int flags;/*package*/ long when;/*package*/ Bundle data;/*package*/ Handler target;/*package*/ Runnable callback;/*package*/ Message next;private static final Object sPoolSync = new Object();private static Message sPool;private static int sPoolSize = 0;
}

what、arg1、arg2这些属性本文不作介绍,我们把目光集中在next、sPoolSync、sPool、sPoolSize这四个静态属性上。

当我们调用Message.obtain()时,返回了一个Message对象。Message对象使用完毕后,调用recycle()方法将其回收。其中obtain方法的代码如下所示:

public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}return new Message();
}

可以看到,obtain方法被调用时,首先检测sPool对象是否为空,若否则将其当做新的message对象返回,并“指向"message对象的next属性,sPoolSize自减。可以看出message对象通过next属性串成了一个链表,sPool为“头指针”。再来看看recycle方法的实现:

public void recycle() {if (isInUse()) {if (gCheckRecycle) {throw new IllegalStateException("This message cannot be recycled because it is still in use.");}return;}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++;}}
}

如果message对象不是处于正在被使用的状态,则会被回收。其属性全部恢复到原始状态后,放在了链表的头部。sPool对象“指向”它,sPoolSize自增。

综上可以看出,通过obtain和recycle方法可以重用message对象。通过操作next、sPoolSync、sPool、sPoolSize这四个属性,实现了一个类似栈的对象池。

msg.target为handler类型,即向handler成员的dispatchMessage方法传入msg参数,其实现如下所示:

public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}
}

这里可以看到回调了各种接口。

到目前为止,我们知道了如何处理在消息队列里面的msg对象,但仍不知道msg对象是如何放到消息队列里面的。通常来说,我们通过Handler的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) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);
}

可知sendMessage最终会调用queue.enqueueMessage(msg, uptimeMillis)将msg对象保存至message queue中,uptimeMillis表示msg执行回调的时刻。 我们来看一下MessageQueue类的enqueueMessage方法:

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("MessageQueue", e.getMessage(), e);msg.recycle();return false;}// 1.设置当前msg的状态msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;// 2.检测当前头指针是否为空(队列为空)或者没有设置when 或者设置的when比头指针的when要前if (p == null || when == 0 || when < p.when) {// 3. 插入队列头部,并且唤醒线程处理msgmsg.next = p;mMessages = msg;needWake = mBlocked;} else {//4. 几种情况要唤醒线程处理消息:1)队列是堵塞的 2)barrier,头部结点无target 3)当前msg是堵塞的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;}}// 5. 将当前msg插入第一个比其when值大的结点前。msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;
}

结合注释,我们可以了解到msg push到queue中时,queue的状态的变化和处理队列的逻辑。

前文中Looper对象的loop方法中:

for (;;) {...Message msg = queue.next(); // 3. 堵塞式在message queue中取数据if (msg == null) {// No message indicates that the message queue is quitting.return;}...msg.target.dispatchMessage(msg); 4. 分发message到指定的target handler...
}

可以看出,message queue的next方法被调用时,可能会发生堵塞。我们来看一看message queue的next方法:

Message next() {// 1. 判断当前loop是否已经使用过,下文会解释这个mPtrfinal long ptr = mPtr;if (ptr == 0) {return null;}int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;// 2. 进入死循环,直到获取到合法的msg对象为止。for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands(); // 这个是什么?}// 3. 进入等待,nextPollTimeoutMillis为等待超时值nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// 4. 获取下一个msgfinal long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {// 当前节点为barrier,所以要找到第一个asynchronous节点do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// 当前队列里最早的节点比当前时间还要晚,所以要进入堵塞状态,超时值为nextPollTimeoutMillisnextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {// 删除当前节点,并返回mBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;if (false) Log.v("MessageQueue", "Returning message: " + msg);return msg;}} else {// 头结点指向nullnextPollTimeoutMillis = -1;}// Process the quit message now that all pending messages have been handled.if (mQuitting) {dispose();return null;}// 5. 如果当前状态为idle(就绪),则进入idle handle的代码块//    进入idle的情况有:队列为空;队列头元素blocking;if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {// 6. 本轮唤醒(next被调用)时没处理任何东西,故再次进入等待。mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// 在一次next调用中,这个代码块只会执行一次for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try {// 如果返回true,则idler被保留,下次next的idle时会被调用。keep = idler.queueIdle();} catch (Throwable t) {Log.wtf("MessageQueue", "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}// Reset the idle handler count to 0 so we do not run them again.pendingIdleHandlerCount = 0;// While calling an idle handler, a new message could have been delivered// so go back and look again for a pending message without waiting.nextPollTimeoutMillis = 0;}
}

代码执行流程见注释。其中IdleHandler是一个接口:

public static interface IdleHandler {boolean queueIdle();
}

IdleHandler提供了一个在MessageQueue进入idle时的一个hook point。更多时与barrier机制一起使用,使message queue遇到barrier时产生一个回调。

总结

前面涉及到的几个主要的类Handler、Looper、MessageQueue和Message的关系如下所述:

  1. Handler负责将Looper绑定到线程,初始化Looper和提供对外API。

  2. Looper负责消息循环和操作MessageQueue对象。

  3. MessageQueue实现了一个堵塞队列。

  4. Message是一次业务中所有参数的载体。

框架图如下所示:

            +------------------+|      Handler     |+----+--------^----+|        |send  |        |  dispatch|        |v        |+----- <---+|          ||  Looper  ||          ||          |+---> -----+|      ^enqueue|      | next|      |+--------v------+----------+|       MessageQueue       |+--------+------^----------+|      |nativePollOnce  |      |   nativeWake|      |
+-----------------v------+---------------------+Lower Layer

最后,留意到MessageQueue中有4个native方法:

// 初始化和销毁
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
// 等待和唤醒
private native static void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
// 判断native层的状态
private native static boolean nativeIsIdling(long ptr);

将会在后续文章中进行介绍。

[Android] Handler源码解析 (Java层)相关推荐

  1. Handler 源码解析(Java 层)

    本文由船员 ChangeHui  自荐,转载发布 从很早开始就认识到 Handler 了,只不过那时修为尚浅,了解的不够深刻,也没有应用自如.不过随着工作时间的增长,对 Handler 又有了更深层次 ...

  2. Handler源码分析 - Java层

    Handler最常见的使用场景就是下载回调,为了不影响用户体验Android不支持在主线程中进行耗时时操作,长时间的耗时操作会产生ANR异常,而下载无疑是耗时操作,所以我们会在子线程中进行下载.但,下 ...

  3. Handler 源码解析——Handler的创建

    前言 Android 提供了Handler和Looper来来满足线程间的通信,而前面我们所说的IPC指的是进程间的通信.这是两个完全不同的概念. Handler先进先出原则,Looper类用来管理特定 ...

  4. Handler全家桶之 —— Handler 源码解析

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 本文首发于本人简书 前言 好记性不如烂笔头. 这是一个系列文章,将会包括: Handler全家桶之 -- Handler 源码解析 ...

  5. android sdk 源码解析

    AndroidSdkSourceAnalysis:https://github.com/LittleFriendsGroup/AndroidSdkSourceAnalysis 第一期 Class 分析 ...

  6. Android Lifecycle源码解析(一)

    Android Lifecycle源码解析(一) 首先我们看HomeActivity中我们添加到一行代码 public class HomeActivity extends AppCompatActi ...

  7. 【Android】Android Broadcast源码解析

    Android Broadcast源码解析 一.静态广播的注册 静态广播是通过PackageManagerService在启动的时候扫描已安装的应用去注册的. 在PackageManagerServi ...

  8. Android xUtils3源码解析之图片模块

    本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...

  9. Android xUtils3源码解析之注解模块

    本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...

最新文章

  1. vue全家桶 ---axios的使用和二次封装
  2. 人脸识别技术大总结(1)——Face Detection Alignment
  3. 【EventBus】事件通信框架 ( 订阅方法注册 | 检查订阅方法缓存 | 反射获取订阅类中的订阅方法 )
  4. bzoj 1011 近似估计
  5. 关于SWT中的布局Layout
  6. android 活动说明,Android – 如何发送GCM推送通知以及要加载哪些活动的说明?
  7. 吊打一切现有开源OCR项目:效果再升7%,速度提升220%
  8. JSP的4大域对象及范围(简)
  9. 如果把Python代码写成这样子就太难看了
  10. 1、视觉slam简介
  11. MKV(Matroska)常见问题浅析
  12. Java读取TXT文件中文输出乱码
  13. MKS-DLC雕刻MKS_TFT_CNC字机器,CNC雕刻,激光雕刻GRBL使用方法
  14. #九、江恩、四维理论方法从古人那里得到的启发(一)来自星空的启示
  15. Aho-Corasick懵逼学习
  16. 【数据分析 R语言实战】学习笔记 第六章 参数估计与R实现(上)
  17. 用c语言做记忆测试小游戏,用C语言实现简单小游戏
  18. bu薪水 华为cloud_【华为】CLOUDBU 华为云数据库工程师招聘 - 软件与微电子学院(SSM)版 - 北大未名BBS...
  19. 长URL链接转短链接算法
  20. python爬取煎蛋网妹子图

热门文章

  1. python运维之轻松模拟开发FTP软件05
  2. Zabbix Linux 客户端安装
  3. vba移动文件_Excel VBA 之 按需求移动、复制文件
  4. 基于ARP的网络扫描工具netdiscover常用命令集合大学霸IT达人
  5. Metasploit Shell升级Meterpreter会话技巧
  6. XamarinSQLite教程下载安装SQLite/SQL Server Compact Toolbox
  7. SQLite中的SELECT子句
  8. Xamarin XAML语言教程模板视图TemplatedView(一)
  9. iOS 9应用开发教程之iOS 9新特性
  10. linux中if语句s,linux 中 if 语句条件的含义