Android的Handler类应该是常用到的,多用于线程间的通信,以及子线程发送消息通知UI线程刷新View等等。这里我主要总结下我对整个消息传递机制,包括Handler,Looper,Message,MessageQueue类的理解。

Looper类:每一个线程都只能拥有一个Looper对象,这里Android是通过将Looper对象设置为当前线程的线程局部变量(这里涉及到ThreadLocal类),可以通过调用Looper.prepare()方法来使当前线程拥有一个Looper对象(主线程不用调用,因为主线程本身已经是拥有Looper对象了)。这里贴出prepare()的源码

  1. public static void prepare() {
  2. prepare(true);
  3. }
  4.  
  5. private static void prepare(boolean quitAllowed) {
  6. if (sThreadLocal.get() != null) {
  7. throw new RuntimeException("Only one Looper may be created per thread"); /*如果当前线程已经拥有Looper对象,就会报错。*/
  8. }
  9. sThreadLocal.set(new Looper(quitAllowed));/*新建Looper对象并设置为当前线程的线程局部变量。*/
  10. }

而Looper类的主要作用是进行消息循环,Looper类维护了一个MesageQueue,通过不停调用loop()方法来取出MeesageQueue中的Message并进行相应的处理。

  1. public static void loop() {
  2. final Looper me = myLooper(); /*得到当前线程的Looper对象*/
  3. if (me == null) {
  4. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  5. }/*如果为空 报异常*/
  6. final MessageQueue queue = me.mQueue;
  7.  
  8. // Make sure the identity of this thread is that of the local process,
  9. // and keep track of what that identity token actually is.
  10. Binder.clearCallingIdentity();
  11. final long ident = Binder.clearCallingIdentity();
  12. /* 进行消息循环 */
  13. for (;;) {
  14. Message msg = queue.next(); // might block
  15. if (msg == null) {
  16. // No message indicates that the message queue is quitting.
  17. return;
  18. }
  19.  
  20. // This must be in a local variable, in case a UI event sets the logger
  21. Printer logging = me.mLogging;
  22. if (logging != null) {
  23. logging.println(">>>>> Dispatching to " + msg.target + " " +
  24. msg.callback + ": " + msg.what);
  25. }
  26.  
  27. msg.target.dispatchMessage(msg);
  28.  
  29. if (logging != null) {
  30. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  31. }
  32.  
  33. // Make sure that during the course of dispatching the
  34. // identity of the thread wasn't corrupted.
  35. final long newIdent = Binder.clearCallingIdentity();
  36. if (ident != newIdent) {
  37. Log.wtf(TAG, "Thread identity changed from 0x"
  38. + Long.toHexString(ident) + " to 0x"
  39. + Long.toHexString(newIdent) + " while dispatching to "
  40. + msg.target.getClass().getName() + " "
  41. + msg.callback + " what=" + msg.what);
  42. }
  43.  
  44. msg.recycle();/* 回收msg(重复利用Message对象) */
  45. }
  46. }

这个留意到loop()方法里对Message的处理方式是调用msg.target.dispatchMessage(msg);,关于这个方法,我们了解完Handler后再看。

MeesageQueue:Message队列,队列就不详解了,先进先出。

Handler:Handler的作用主要是分发信息 相关方法有:

post(Runnable)
        postAtTime(Runnable,long)
        postDelayed(Runnable long)
        sendEmptyMessage(int)
        sendMessage(Message)
        sendMessageAtTime(Message,long)
        sendMessageDelayed(Message,long)

  1. public final boolean sendMessage(Message msg)
  2. {
  3. return sendMessageDelayed(msg, 0);
  4. }
  5.  
  6. public final boolean sendEmptyMessage(int what)
  7. {
  8. return sendEmptyMessageDelayed(what, 0);
  9. }
  10.  
  11. public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
  12. Message msg = Message.obtain();
  13. msg.what = what;
  14. return sendMessageDelayed(msg, delayMillis);
  15. }
  16.  
  17. public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
  18. Message msg = Message.obtain();
  19. msg.what = what;
  20. return sendMessageAtTime(msg, uptimeMillis);
  21. }
  22.  
  23. public final boolean sendMessageDelayed(Message msg, long delayMillis)
  24. {
  25. if (delayMillis < 0) {
  26. delayMillis = 0;
  27. }
  28. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  29. }
  30.  
  31. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  32. MessageQueue queue = mQueue;
  33. if (queue == null) {
  34. RuntimeException e = new RuntimeException(
  35. this + " sendMessageAtTime() called with no mQueue");
  36. Log.w("Looper", e.getMessage(), e);
  37. return false;
  38. }
  39. return enqueueMessage(queue, msg, uptimeMillis);
  40. }

发现这些方法最后都是调用enqueueMessage(),看看这个方法的源码

  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  2. msg.target = this;
  3. if (mAsynchronous) {
  4. msg.setAsynchronous(true);
  5. }
  6. return queue.enqueueMessage(msg, uptimeMillis);
  7. }

调用了MessageQueue的enqueueMessage方法,将Message对象加入了队列中去

再看看这几个方法

  1. public final boolean post(Runnable r)
  2. {
  3. return sendMessageDelayed(getPostMessage(r), 0);
  4. }
  5.  
  6. public final boolean postAtTime(Runnable r, long uptimeMillis)
  7. {
  8. return sendMessageAtTime(getPostMessage(r), uptimeMillis);
  9. }
  10.  
  11. public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
  12. {
  13. return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
  14. }
  15.  
  16. public final boolean postDelayed(Runnable r, long delayMillis)
  17. {
  18. return sendMessageDelayed(getPostMessage(r), delayMillis);
  19. }

发现post()系列的方法最后都是调用sendMessage系列的方法只是传入的Message对象有所不同,我们再看看getPostMessage()

  1. private static Message getPostMessage(Runnable r) {
  2. Message m = Message.obtain();
  3. m.callback = r;
  4. return m;
  5. }
  6.  
  7. private static Message getPostMessage(Runnable r, Object token) {
  8. Message m = Message.obtain();
  9. m.obj = token;
  10. m.callback = r;
  11. return m;
  12. }

发现其实就是把Runnable对象赋给Message的callback。通过以上代码的分析,基本上对整个消息机制有个概念了,当我们调用Handler的sendMessage()或post()等一系列方法时,Handler会将消息加入到MessageQueue中,这个MessageQueue为Handler对应的Looper对象所维护的queue(创建Handler时可以传入Looper对象,无参情况下为当前线程的Looper,想具体了解可以看下Handler的构造函数,这里不再赘述)然后由Looper类的loop()方法将消息取出。并做处理。至于怎么处理,我们就要看下msg.target.dispatchMessage(msg)这个方法了。我们知道msg的target是一个Handler对象,看下这个方法的源码

  1. public void dispatchMessage(Message msg) {
  2. /* 如果callback不为空,即发送消息的时候传入了Runnable对象,也就是调用的是post()系列的方法。那么就会执行handleCallback()这个方法,否则执行handleMessage() */
  3. if (msg.callback != null) {
  4. handleCallback(msg);
  5. } else {
  6. if (mCallback != null) {
  7. if (mCallback.handleMessage(msg)) {
  8. return;
  9. }
  10. }
  11. handleMessage(msg);
  12. }
  13. }

handleMessge()方法想必大家都很熟悉,这个方法是我们new一个Handler对象时常常会去重写的,然后处理消息时会回调这个方法。再看看handleCallback()

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

代码很简单,就是调用了callback的run()方法。这里要特别注意的是调用的是run()而不是start(),这意味着将不会另起一个线程。而是在Looper对象所在的线程执行。至于为什么这么做笔者认为由于这样做即使是多次post也不需要开启多个线程,大大提高了程序的性能,同时,在某些情况下,我们希望能够顺序执行callback,那么使用这个方法无疑是非常棒的。当然,如果是要并发执行作那么就得使用其他法子了。总的来说post()方法相当于sendMessage()+handleMessage()使用方式的一种变体。

Handler Looper源码解析(Android消息传递机制)的更多相关文章

  1. Android Handler机制(三)----Looper源码解析

    一.Looper Looper对象,顾名思义,直译过来就是循环的意思,从MessageQueue中不断取出message. Class used to run a message loop for a ...

  2. Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)

    不要心急,一点一点的进步才是最靠谱的. 读完本文你将了解: 前言 Message 如何获取一个消息 Messageobtain 消息的回收利用 MessageQueue MessageQueue 的属 ...

  3. 源码解析Android中View的measure量算过程

    Android中的Veiw从内存中到呈现在UI界面上需要依次经历三个阶段:量算 -> 布局 -> 绘图,关于View的量算.布局.绘图的总体机制可参见博文< Android中View ...

  4. Netty源码解析 -- 事件循环机制实现原理

    本文主要分享Netty中事件循环机制的实现. 源码分析基于Netty 4.1 EventLoop 前面分享服务端和客户端启动过程的文章中说过,Netty通过事件循环机制(EventLoop)处理IO事 ...

  5. Netty源码解析 -- 零拷贝机制与ByteBuf

    本文来分享Netty中的零拷贝机制以及内存缓冲区ByteBuf的实现. 源码分析基于Netty 4.1.52 Netty中的零拷贝 Netty中零拷贝机制主要有以下几种 1.文件传输类DefaultF ...

  6. Android Handler机制(四)---Handler源码解析

    Handler的主要用途有两个:(1).在将来的某个时刻执行消息或一个runnable,(2)把消息发送到消息队列. 主要依靠post(Runnable).postAtTime(Runnable, l ...

  7. Android 进阶16:IntentService 使用及源码解析

    It's time to start living the life you've only imagined. 读完本文你将了解: IntentService 简介 IntentService 源码 ...

  8. Android 开源项目源码解析(第二期)

    Android 开源项目源码解析(第二期) 阅读目录 android-Ultra-Pull-To-Refresh 源码解析 DynamicLoadApk 源码解析 NineOldAnimations ...

  9. Android 进阶15:HandlerThread 使用场景及源码解析

    眼睛困得要死,但今天的计划不完成又怎么能睡呢?明日复明日,明日何其多啊! 读完本文你将了解: HandlerThread 简介 HandlerThread 源码 HandlerThread 的使用场景 ...

随机推荐

  1. linux中常用的命令大全

    系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...

  2. python+tushare获取上市公司财务报表:资产负债表

    接口:balancesheet 描述:获取上市公司资产负债表 积分:用户需要至少500积分才可以调取,具体请参阅本文最下方积分获取办法 注:tushare包下载和初始化教程,请查阅我之前的文章 提示: ...

  3. Spring mvc的执行流程

    一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml中指定), WEB 容器将该请求转交给 DispatcherServlet 处理 DispatcherSe ...

  4. 解决虚拟机克隆的linux系统ip无法正常使用问题

    当我们克隆centos虚拟机无法正常获取IP地址,重启网卡也提示Bringing up interface eth0:  Device eth0 does not seem to be present ...

  5. Codeforces 19E&BZOJ 4424 Fairy(好题)

    日常自闭(菜鸡qaq).不过开心的是看了题解之后1A了.感觉这道题非常好,必须记录一下,一方面理清下思路,一方面感觉自己还没有完全领会到这道题的精髓先记下来以后回想. 题意:给定 n 个点,m 条边的 ...

  6. informix 计算 日期之差

    原文地址:http://blog.chinaunix.net/uid-678894-id-3138829.html https://blog.csdn.net/zhengqiqiqinqin/arti ...

  7. C#接口的作用实例解析

    一.接口的作用: 我们定义一个接口: public interface IBark { void Bark(); } 1.  再定义一个类,继承于IBark,并且必需实现其中的Bark()方法 pub ...

  8. Java Number&Math类

    Java Number类 一般地,当需要使用数字的时候,我们通常使用内置数据类型,如:byte.int.long.double等. 实例 int i = 5000; float gpa = 13.65 ...

  9. Yii2数据库操作 事务

    Yii2 DAO http://blog.csdn.net/hzqghost/article/details/44116039

  10. sql 修改数据

    关系数据库的基本操作就是增删改查,即CRUD:Create.Retrieve.Update.Delete.其中,对于查询,我们已经详细讲述了SELECT语句的详细用法. 而对于增.删.改,对应的SQL ...