前言

一问起Android应用程序的入口,很多人会说是Activity中的onCreate方法,也有人说是ActivityThread中的静态main方法。因为Java虚拟机在运行的时候会自动加载指定类的静态共有main方法,因此个人更倾向于第二种说法。

  1. public final class ActivityThread { 

  2. ...... 

  3. public static void main(String[] args) { 

  4. ...... 

  5. Looper.prepareMainLooper(); 

  6. ActivityThread thread = new ActivityThread(); 

  7. thread.attach(false); 

  8. if (sMainThreadHandler == null) { 

  9. sMainThreadHandler = thread.getHandler(); 



  10. ...... 

  11. Looper.loop(); 

  12. throw new RuntimeException("Main thread loop unexpectedly exited"); 





我们注意到这段代码,先调用Looper的prepareMainLooper()方法,新建一个ActivityThread,然后再获取MainThreadHander,最后调用Looper.loop()方法,程序就一直在loop方法中循环。Looper,Handler之间有何关系?请看下文。

程序的消息循环

  1. class test { 

  2. public static void main (String[] args) { 

  3. System.out.println("hello world!"); 





上述小程序就是一个任务,虚拟机启用一个线程执行完该任务后,程序就结束了。为了保证程序不立即退出,一般都会写一个循环

  1. class test { 

  2. public static void main (String[] args) { 

  3. while (hasNextMessage()) { 

  4. msg = getMessage() ;  

  5. handleMessage(msg) ; 







系统不断从getMessage获取消息,再通过handleMessage来处理消息。这种基于消息的循环模型在许多的系统框架中都有实现。比如iOS系统中的RunLoop,再比如windows系统中的消息队列,windows系统会为每一个UI线程分配一个消息队列,发生输入事件后,windows将事件转换为一个"消息"发送给系统消息队列,操作系统有一个专门的线程从系统消息队列中取出消息,分发给各个UI线程的输入消息队列中。Android中的应用系统框架也不例外,也有一套自己的消息循环机制,这套机制是靠Looper、Handler、MessageQueue来共同完成的。

原理

p1.jpg

图片来自zongpeiqing的CSDN博客。Looper负责消息循环(例子中的while语句),Handler负责发送和处理消息,MessageQueue则负责管理消息(消息的增加和移除)。不管是sendMessage还是sendMessageDelay或是View.post方法,或是上面列出的没列出的发送消息的方法,最终都会包装成一条消息Message(这条消息由Handler发出),然后调用MessageQuque的enqueueMessage方法把消息放到消息队列MessageQueue中。而Looper则会不停地查看MessageQueue中是否有新消息msg,有新消息就会调用新消息msg.target.handleMessage()去处理消息,否则会一直阻塞。msg.target实际上是Handler对象。因此,Handler发送的消息,最终也是由Handler来处理。

问题

Q1: Handler是怎么在不同线程之间切换的?

  1. public final class Looper { 

  2. ...... 

  3. public static void loop() { 

  4. final Looper me = myLooper(); 

  5. if (me == null) { 

  6. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 



  7. final MessageQueue queue = me.mQueue; 

  8. ........ 

  9. for (;;) { 

  10. Message msg = queue.next(); // might block 

  11. if (msg == null) { 

  12. // No message indicates that the message queue is quitting. 

  13. return; 



  14. ...... 

  15. msg.target.dispatchMessage(msg); 

  16. ...... 

  17. msg.recycleUnchecked(); 







  1. public class Handler { 

  2. /* 

  3. * Set this flag to true to detect anonymous, local or member classes 

  4. * that extend this Handler class and that are not static. These kind 

  5. * of classes can potentially create leaks. 

  6. */ 

  7. private static final boolean FIND_POTENTIAL_LEAKS = false; 

  8. private static final String TAG = "Handler"; 


  9. /** 

  10. * Callback interface you can use when instantiating a Handler to avoid 

  11. * having to implement your own subclass of Handler. 



  12. * @param msg A {@link android.os.Message Message} object 

  13. * @return True if no further handling is desired 

  14. */ 

  15. public interface Callback { 

  16. public boolean handleMessage(Message msg); 




  17. /** 

  18. * Subclasses must implement this to receive messages. 

  19. */ 

  20. public void handleMessage(Message msg) { 




  21. /** 

  22. * Handle system messages here. 

  23. */ 

  24. public void dispatchMessage(Message msg) { 

  25. if (msg.callback != null) { 

  26. handleCallback(msg); 

  27. } else { 

  28. if (mCallback != null) { 

  29. if (mCallback.handleMessage(msg)) { 

  30. return; 





  31. handleMessage(msg); 







首先在线程1新建一个handler,在线程2 新建一条消息msg,然后在线程2调用hander.sendMessage(msg),因为在handler的处理逻辑handleMessage()方法是放在线程1的,因此在线程2中调用了hander.sendMessage(msg),MessagQueue插入了这条消息,Looper发现有新消息,然后取出新消息,调用msg.target.dispatchMessage(msg),上面已经说到,target其实是hander,这样便成功地切换到线程1的handleMessage逻辑上来了。最常见的例子就是在Activity中声明一个Handler,然后异步线程去请求网络,再通过网络更新UI。可以参考这里

Q2: 上面的模型中,为何不直接使用Handler来循环消息?

这个问题仁者见仁,智者见智,这只是一种实现消息循环的方法之一,而不是唯一方法。不是说一定要用Looper。

Q3: 需要注意的点?

使用Looper前一定要调用Looper.prepare生成线程内(TheadLocal存储)的Looper实例,然后才能调用Looper.loop()实现消息循环。下面是一个非主线程中Looper和Handler演示例子,此时整个线程是不会自动停止的,会一直阻塞,直到调用了Looper.quit()方法才会停止。(下面代码仅仅是为了演示,没有做任何事)

  1. new Thread("example-1") { 

  2. Handler handler = new Handler() { 

  3. @Override 

  4. public void handleMessage(Message msg) { 

  5. // 自己的处理逻辑 

  6. if (条件满足) { 

  7. Looper.quit() ; //退出消息循环,结束线程 





  8. } ; 

  9. @Override 

  10. public void run() { 

  11. Looper.prepare(); 

  12. // do something.... 

  13. Message msg = Message.obtain(); 

  14. msg.what = ... 

  15. msg.obj = ... 

  16. ....... 

  17. handler.sendMessage(msg); 

  18. Looper.loop(); 



  19. }.start() ; 

Q4: 为何ActivityThread中的Looper.loop()没有阻塞主线程?

。。。。。

参考

《Android开发艺术探索》

Android Handler 消息循环机制的更多相关文章

  1. Android的消息循环机制 Looper Handler类分析

    Android的消息循环机制 Looper Handler类分析 Looper类说明   Looper 类用来为一个线程跑一个消息循环. 线程在默认情况下是没有消息循环与之关联的,Thread类在ru ...

  2. Android HandlerThread 消息循环机制之源代码解析

    关于 HandlerThread 这个类.可能有些人眼睛一瞟,手指放在键盘上,然后就是一阵狂敲.立即就能敲出一段段华丽的代码: HandlerThread handlerThread = new Ha ...

  3. 安卓中的消息循环机制Handler及Looper详解

    我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handl ...

  4. System、应用程序进程的Binder线程池和Handler消息循环

    首先看一张Android系统启动流程图:

  5. Dart异步与消息循环机制

    Dart与消息循环机制 翻译自https://www.dartlang.org/articles/event-loop/ 异步任务在Dart中随处可见,例如许多库的方法调用都会返回Future对象来实 ...

  6. 【Dart学习】-- Dart之消息循环机制[翻译]

    概述 异步任务在Dart中随处可见,例如许多库的方法调用都会返回Future对象来实现异步处理,我们也可以注册Handler来响应一些事件,如:鼠标点击事件,I/O流结束和定时器到期. 这篇文章主要介 ...

  7. Win32消息循环机制等【转载】http://blog.csdn.net/u013777351/article/details/49522219

    Dos的过程驱动与Windows的事件驱动 在讲本程序的消息循环之前,我想先谈一下Dos与Windows驱动机制的区别: DOS程序主要使用顺序的,过程驱动的程序设计方法.顺序的,过程驱动的程序有一个 ...

  8. Android Handler消息机制不完全解析

    1.Handler的作用 Android开发中,我们经常使用Handler进行页面的更新.例如我们需要在一个下载任务完成后,去更新我们的UI效果,因为AndroidUI操作不是线程安全的,也就意味着我 ...

  9. Android Handler消息机制源码解析

    好记性不如烂笔头,今天来分析一下Handler的源码实现 Handler机制是Android系统的基础,是多线程之间切换的基础.下面我们分析一下Handler的源码实现. Handler消息机制有4个 ...

随机推荐

  1. VS2017+EF+Mysql生成实体数据模型(解决闪退的坑)

    原文:VS2017+EF+Mysql生成实体数据模型(解决闪退的坑) 最近要使用VS2017+EF+Mysql,在生成实体数据模型踏过一些坑,在此做个总结. 1.先下载并安装 mysql-connec ...

  2. Scala 基础(6)—— 控制结构

    1. Scala 的内建控制结构 Scala 有几个内建的控制结构,包括: if 表达式 while 循环和 do-while 循环 for 表达式 try 表达式 match 表达式 Scala 的 ...

  3. 解决IE浏览器中出现“Resource interpreted as Document but transferred with MIME type application/json”问题

    在上传图片时,使用ajax提交,返回的数据格式为json.在测试时发现IE浏览器中,上传图片后,没有显示图片,而是弹出一个提示:是否保存UploadImg.json文件:而在其他浏览器中正常. 在Ch ...

  4. weex 开发 (已放弃了)

    关于weex 开发 本菜已放弃使用了,当初选择使用weex 是为了同时支持h5 和 android / ios  三端:想法很不错,深入之后 突然发现,开发起来并没有很轻松,因为weex 中有些方法, ...

  5. Codeforces 359D Pair of Numbers | 二分+ST表+gcd

    题面: 给一个序列,求最长的合法区间,合法被定义为这个序列的gcd=区间最小值 输出最长合法区间个数,r-l长度 接下来输出每个合法区间的左端点 题解: 由于区间gcd满足单调性,所以我们可以二分区间 ...

  6. 【HDU 2594 Simpsons' Hidden Talents】

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission( ...

  7. code forces 994B

    B. Knights of a Polygonal Table time limit per test 1 second memory limit per test 256 megabytes inp ...

  8. J2SE总结(一)-------容器

    最近大家都在讨论容器以及如何在项目中去实际的应用它,由于之前对容器没有什么概念,所以把J2SE里面讲的容器的一些基础知识看了一下,总结一下最基本的东西. 围绕整章最核心的就属下面这张图了吧. 一.概念 ...

  9. 【Android开发日记】之入门篇(十)——Android应用配置文件解析

    在Android基于组件的应用设计架构中,配置文件是一个很重要的元素.它将应用所包含的组件.各组件的能力和配置以及应用环境介绍给Android框架层的各个服务,让Android知道如何去调度应用中的各 ...

  10. 51Nod 1561 另一种括号序列

    题目链接 分析: 卡常数~~~好气啊~~~这是看脸的时代啊~~~ $A$代表$($的数量,$B$代表$)$的数量... 如果$($的数量多于$)$的数量,那么最有方案显然是添加$A-B$个$)$... ...