参考文章:
http://gityuan.com/2015/12/26/handler-message-framework/#next
参考资料:
Android Framework的源码:
Message.java
MessageQueue.java
Looper.java
Handler.java
ThreadLocal.java
(以上几个类代码都不复杂,自己去打开看一看还是有必要的)

1.前言

在Android开发中,Handler是非常常用的,如果多线程需要切换到主线程更新UI,通常都会使用到Handler。Handler后面的是Android的消息机制。

2.思考

2.1为什么Android需要一套通用的多线程通信的机制?

Android的UI是单线程机制(从效率,实现的复杂程度来说,是最优解),这意味着
我们只能在UI线程(主线程)更新UI,如果在其他线程更新UI,app会报异常(奔溃)
然而多线程是Android开发中非常常见的情景,不可能把所有代码都运行在主线程,如网络请求,文件读写,复杂的运算,这些操作如果运行在主线程,会造成UI线程卡顿,甚至会带来ANR(应用无响应)的问题。
当我们在子线程执行完任务后,需要把结果显示在UI上,就必然涉及到多线程的通信。
多线程通信的实现方式很多,但是,Android 系统提供了一套已经实现的通用的机制,这是最好的选择,同时这对开发者也是方便友好的。

2.2为什么Android采用的消息驱动多线程通信的机制?

由于多线程(我们只考虑同一个进程下的多线程),共享进程的内存空间,所以它们之间的相互操作非常简单
我们可以使用回调监听,或者更粗暴一点,一个A线程直接持有B线程的引用,然后就可直接通知操作B线程
唯一的问题是:通用性
不可能穷尽所有线程之间通信的可能,所以,理想的解决方法是抽象:
把线程之间的通信抽象为A线程发出消息通知,B处理消息
消息Message代表两个线程之间的通信传递的内容
以上,线程之间的通信问题就抽象为由一个线程发出消息Message,另一个线程接收并处理这个Message问题

2.3为什么Android Loop循环不会卡死CPU?

对于一个App来说:
它需要一直运行,直至被退出,结束进程
最简单的方式就是一个死循环(永真循环),注意为了不让这个死循环一直占用CPU,所以必然有一个等待并让出cpu的处理。
对应Android来说,每一个程序的入口是:
ActivityThread中的main方法(我们在学习Java里面非常熟悉的main方法,这是整个App的入口):

  public static void main(String[] args) {Looper.prepareMainLooper();
//注意这个方法,这个方法为当前的线程创建了一个属于当前线程的Looper,并赋值给 Looper的静态变量 private static Looper sMainLooper; ActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}Looper.loop();//这里是一个死循环throw new RuntimeException("Main thread loop unexpectedly exited");}

在Looper中的loop()方法:

 public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;//ongoing这里可以看到一个Looper里面包含着一个对应的MessageQueue// 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,这个方法可能会被阻塞,等待,让出CPUif (msg == null) {// No message indicates that the message queue is quitting.return;}try {msg.target.dispatchMessage(msg);} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();msg.recycleUnchecked();}}

3.Java层研读:

3.1理清下面几个类的作用以及它们之间的联系:

首先是它们的整体关系:使用Gityuan大神的图片:

我们使用一个最常见的的场景解析它们的关系:
UI线程A:刷新,显示UI
工作线程B:负责请求网络,获取一个结果,假设结果是一个字符串 "Hello ,thread A,I 'm thread B."
现在我们需要把结果从工作线程B传递到UI线程A,然后由UI线程把这个结果显示到一个Textview的控件上
分为三个步骤:
在主(UI)线程 初始化一个Handler mHandler对象

在工作线程中
通过
Message message=mHandler.obtain();
获取一个Message
这个时候可以把"Hello ,thread A,I 'm thread B." 保存在Message里面
调用mHandler.sendMessage()方法,就会把Message保存到主线程的Looper的 MessageQueue中

主线程的Looper会不断循环从MessageQueue中取出Message,然后通过dispatchMessage方法分发给它对应的Handler处理

3.2Message:

Message代表一个从一个线程传到另外一个线程的消息
Android的消息驱动多线程通信的机制是基于Message的
所以Message需要重点关心的是Message所能包含的信息,也就是这个类的
成员变量
源码:

public final class Message implements Parcelable {/*** User-defined message code so that the recipient can identify* what this message is about. Each {@link Handler} has its own name-space* for message codes, so you do not need to worry about yours conflicting* with other handlers.*/public int what;//用于区分Message类型的id,作用域是单个Handler,也就是不同的Handler这个值即使重复也没关系/*** arg1 and arg2 are lower-cost alternatives to using* {@link #setData(Bundle) setData()} if you only need to store a* few integer values.*/public int arg1;//arg1,一个位置,供使用者保存一个整形的值/*** arg1 and arg2 are lower-cost alternatives to using* {@link #setData(Bundle) setData()} if you only need to store a* few integer values.*/public int arg2;/arg2,一个位置,供使用者保存一个整形的值/*** An arbitrary object to send to the recipient.  When using* {@link Messenger} to send the message across processes this can only* be non-null if it contains a Parcelable of a framework class (not one* implemented by the application).   For other data transfer use* {@link #setData}.** <p>Note that Parcelable objects here are not supported prior to* the {@link android.os.Build.VERSION_CODES#FROYO} release.*/public Object obj;/*** Optional Messenger where replies to this message can be sent.  The* semantics of exactly how this is used are up to the sender and* receiver.*/public Messenger replyTo;/*** Optional field indicating the uid that sent the message.  This is* only valid for messages posted by a {@link Messenger}; otherwise,* it will be -1.*/public int sendingUid = -1;/** If set message is in use.* This flag is set when the message is enqueued and remains set while it* is delivered and afterwards when it is recycled.  The flag is only cleared* when a new message is created or obtained since that is the only time that* applications are allowed to modify the contents of the message.** It is an error to attempt to enqueue or recycle a message that is already in use.*//*package*/ static final int FLAG_IN_USE = 1 << 0;/** If set message is asynchronous *//*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;/** Flags to clear in the copyFrom method *//*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;/*package*/ int flags;//状态标记位/*package*/ long when;//记录当前Message需要被处理的时间/*package*/ Bundle data;//保存一个类型为Bundle的数据/*package*/ Handler target;//记录当前的Message的处理者(目的地)/*package*/ Runnable callback;//记录当前的Message的Runnable回调/*下面的代码使得我们可以用多个Message构建一个单链表// sometimes we store linked lists of these things/*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;//为了避免频繁创建销毁Message,设置一个最大size为50的Message对象池private static boolean gCheckRecycle = true;
}

3.2MessageQueue:

MessageQueue是消息机制的Java层和C++层的连接纽带,大部分核心方法都交给native层来处理,
这个需要结合C++ Native层的NativeMessageQueue来看。
具体请参考Gityuan大神的分析文章:
http://gityuan.com/2015/12/27/handler-message-native/

3.4Looper:

Looper负责从MessageQueue不断取出需要处理的Message,然后把Message分发给它对应的Handler处理
重要的方法有以下三个:
Looper.prepare()

/** Initialize the current thread as a looper.* This gives you a chance to create handlers that then reference* this looper, before actually starting the loop. Be sure to call* {@link #loop()} after calling this method, and end it by calling* {@link #quit()}.*/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));//这个方法就为当前的线程set一个属于它的Looper,ThreadLocal见另外一篇文章的分析}

Looper.prepareMainLooper()
这个方法本质也是使用了Looper.prepare(),这个方法只在UI线程被调用,然后
把Ui线程对应的Looper也赋值给了sMainLooper

  /*** 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);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}

Looper.loop()
这个方法是一个死循环,它负责从MessageQueue中取出需要处理的Message,并分发处理

 public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}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 可能阻塞,然后会让出CPU给其他线程(进程)if (msg == null) {// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long traceTag = me.mTraceTag;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}try {msg.target.dispatchMessage(msg);//把当前的Message分发给对应的Handler处理} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {Log.wtf(TAG, "Thread identity changed from 0x"+ Long.toHexString(ident) + " to 0x"+ Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "+ msg.callback + " what=" + msg.what);}msg.recycleUnchecked();}}

3.5Handler:

Handler是我们最经常使用的:
它有两个职责:

  1. 发送Message
  2. 处理Message
    通常我们会继承并重写它的 handleMessage(Message msg)方法,因为这个方法在父类只是一个空实现:
  /*** Subclasses must implement this to receive messages.*/public void handleMessage(Message msg) {}

在一个需要接收处理Message的线程初始化一个Handler

在需要发送Message的线程利用这个Handler发送消息(本质把Message添加到接收线程的Looper的MessageQueue中)

4.代码赏析:

4.1在Message类中,必须考虑一个这样的问题,每一次都new 一个Message实例,有时是非常低效并且消耗资源的。Java虽然有Gc机制,但是这个并不能保证及时回收。

解决问题的方法之一就是对象池:
我们短时间内需要获取很多对象,这些对象又很快使用完毕,可以考虑重用对象。
考虑到对象池的数目大小是不断变化的,插入删除操作比较多,考虑使用单链表的数据结构

private static Message sPool;//静态全局变量,记录链表头
private static int sPoolSize = 0; //静态全局变量,记录链表长度
private static final int MAX_POOL_SIZE = 50;//常量,对象池最大值/*** Return a new Message instance from the global pool. Allows us to* avoid allocating new objects in many cases.*/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();}/*** Recycles a Message that may be in-use.* Used internally by the MessageQueue and Looper when disposing of queued Messages.*/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的对象,通过全局静态变量
Message sPool 和一个int sPoolSize 记录一个单链表就完成了一个简单的对象池(抽象意义上的)

  1. 第一次通过 obtain()方法获取Message对象,注意,此时sPool==null,所以直接new 一个 Message实例
  2. 如果sPool==null,调用多少次obtain()方法都会new 多少个Message实例(因为没有对象池是空的)
  3. 注意:一个Message实例被使用后,将会调用recycleUnchecked()方法,这个时候会把Message的值重设
    然后,做判断,如果对象池的数目没有超过最大值,就把这个对象插入到单链表的头部(回收),并且把sPollSzie++
    4.如果sPool !=null(sPool已经初始化) 此时调用obtain方法,
  Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;

会从单链表头部取一个Message 实例,并且把sPollSize--

5.能不能模仿实现一个多线程消息机制

由于这个涉及到Linux的epoll机制,需要理解这个并使用C,C++调用,目前还个人还完成不了。
留给大家一起想一下吧。
具体解析参考:
http://gityuan.com/2015/12/27/handler-message-native/

http://gityuan.com/2015/12/06/linux_epoll/

转载于:https://www.cnblogs.com/bylijian/p/7344898.html

Android的消息机制简单总结相关推荐

  1. Android异步消息机制

    2019独角兽企业重金招聘Python工程师标准>>> 目录介绍 1.Handler的常见的使用方式 2.如何在子线程中定义Handler 3.主线程如何自动调用Looper.pre ...

  2. 聊一聊Android的消息机制

    2019独角兽企业重金招聘Python工程师标准>>> 聊一聊Android的消息机制 侯 亮 1概述 在Android平台上,主要用到两种通信机制,即Binder机制和消息机制,前 ...

  3. Android进阶知识树——Android Handler消息机制

    1.概述 在安卓程序启动时,会默认在主线程中 运行程序,那如果执行一些耗时的操作则UI就会处于阻塞状态,出现界面卡顿的现象,再者用户的多种操作,系统是如何做到一一处理的,系统又是如何管理这些任务的,答 ...

  4. Android 开发艺术探索——第十章 Android的消息机制

    Android 开发艺术探索--第十章 Android的消息机制读书笔记 Handler并不是专门用于更新UI的,只是常被用来更新UI 概述 Android的消息机制主要值得就是Handler的运行机 ...

  5. 【安卓学习笔记】Android Handler 消息机制探究

    一.概述 1.android消息机制的含义: Android消息机制,其实指的就是 Handler 的运行机制,而 Handler 要正常运作,又需要底层的 MessageQueue , Looper ...

  6. Android的消息机制(2)

    上一节中,是主线程自己发了一个消息到自己的Message Queue中,并把消息从队列中提取出来.那么如何由别的线程发送消息给主线程的Message Queue中呢? 直接看代码~~ 1 2 3 4 ...

  7. Android的消息机制

    Android的消息机制(一) android 有一种叫消息队列的说法,这里我们可以这样理解:假如一个隧道就是一个消息队列,那么里面的每一部汽车就是一个一个消息,这里我们先忽略掉超车等种种因素,只那么 ...

  8. 《Android开发艺术探索》读书笔记 (10) 第10章 Android的消息机制

    第10章 Android的消息机制 10.1 Android消息机制概述 (1)Android的消息机制主要是指Handler的运行机制,其底层需要MessageQueue和Looper的支撑.Mes ...

  9. 【学习】Android的消息机制

    Android的消息机制主要指Handler的运行机制,而Handler的运行需要MessageQueue和Looper支撑 前置知识 MessageQueue:消息队列,内部以单链表的形式存储消息列 ...

最新文章

  1. Mybatis批量添加对象List
  2. 架设SharePoint工作组网站(上)
  3. 1112 Stucked Keyboard (20 分)【难度: 一般 / 知识点: 模拟】
  4. 各大网站CSS代码初始化集合
  5. 【渝粤教育】国家开放大学2018年春季 0177-22T电机学(二) 参考试题
  6. 【信号】函数kill、raise、abort、alarm
  7. Yolo系列知识点梳理(Yolov1-v5)
  8. 【springboot】application.yml配置文件中数据库密码password加密后显示
  9. 运行gedit报No protocol specified
  10. 安装Ubuntu镜像和VMware在安装Ubuntu镜像之后开机蓝屏的解决方案
  11. EF Code First Migrations数据库迁移 (转帖)
  12. Oracle常见的Hint(二)
  13. 教你轻松安装Adobe Acrobat XI Pro(编辑PDF使用的)
  14. IBM X System ServerGuide 8.41 服务器 系统安装 引导盘图文教程
  15. knockoutjs
  16. 机器学习项目实践总结 -- 24个机器学习最佳入门项目(附源代码)
  17. 利用assimp显示gltf
  18. 判断是否打开相机权限,如果没有打开相机权限
  19. Office Professional Plus 2010 产品密钥
  20. pytorch下可训练分段函数的写法

热门文章

  1. linux whois工具,CentOS如何安装whois命令
  2. mysql 启动 failed to start_Linux下启动MySQL提示“mysql deamon failed to start”错误的解决办法...
  3. java设计一个顺序表类的成员函数_顺序表代码讲解以及实现
  4. 服务器清理c盘日志文件,清理WIN2003服务器C盘垃圾的批处理
  5. windows下用elasticdump导入json数据到Elasticsearch中
  6. 入职3个月的Java程序员面临转正,挑战大厂重燃激情!
  7. python【力扣LeetCode算法题库】2-两数相加
  8. Keras【Deep Learning With Python】—Keras实现序贯模型
  9. php 数组与数组之间去重,PHP开发中一维数组与二维数组去重功能实现教程
  10. 分峰截幅c语言算法,面向桥梁健康监测的复合传感技术研究