一,前言

众多周知, Android 只允许在主线程中更新UI,因此主线程也称为UI线程(ActivityThread)。

如此设计原因有二:

(1) 由于UI操作的方法都不是线程安全的,如果多个线程都可以更新UI,会造成UI界面混乱。

(2)UI操作都加锁,线程同步的话,非常影响性能。

故Android干脆只允许在单个线程-->主线程中操作UI, 否则会报android.view.ViewRootImpl$CalledFromWrongThreadException:只有创建该view的线程才可以改变该view的状态。

Google 为上述单一线程更新UI设计了Handler消息机制,主要场景是为了更新UI,更广泛的应用场景是线程之间进行通信。

二, Handler消息机制

Handler 消息机制主要包含三个重要角色:Handler, MessageQueue, Looper

Handler 发送消息,压入Messagequeue,Looper 作为轮询器,不停地从MessageQueue中获取Message,并再次分发dispatchMessage给Handler, 由handler的handleMessage()方法处理。

1, Handler  消息发送处理者

         Handler持有一个Looper对象和一个MessageQueue对象(即looper.mQueue), 在创建Handler对象时可以通过指定Looper(如果不指定,默认为当前线程的Looper对象 ),来确定Handler为哪个目标线程服务: 发送消息到该目标线程,并在目标线程里处理消息。 

        Handler提供了多种发送消息的方法,它们之间的调用关系如下

在post(Runnable r)   / postDelayed(Runnable r,  long delay)  / sendMessage(msg)里调用的是sendMessageDelayed(msg, delay ) ,进而sendMessgaeAtTime(msg, SystemClock.uptimeMillis()+ delay)。

其中post方法是将runnable 通过getPostMessageRunnable(r) 转化为一个Message对象,msg.callback = r;

最终通过enqueueMessage()将消息压入队列中。可以看到此处将message.target 指定为该handler,(后面会讲target的作用),然后调用了MessageQueue.enquqeueMessage()

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){

msg.target = this;

return queue.enqueueMessage(msg, uptimeMillis);

}

另外Handler提供了可以将消息发送到队头的特殊方法, 这样可以使得消息可以优先被处理,postAtFrontOfQueue(msg)、 sendMessageAtFront|OfQueue(msg), 其实本质也一样,只不过消息的时间戳设为0, 最终是sendMessgaeAtTime(msg, 0)

2, Looper 轮询者

Looper作为轮询器,提供的都是静态方法。

(1)   Looper.prepare() 初始化

内部创建一个Looper对象,并存在ThreadLocal里。ThreadLocal是线程的数据隔离区,可以存放各个线程单独的数据,存取ThreadLocal数据,不受多线程影响。 new Looper()时,looper对象持有一个新建的MessageQueue对象 和 当前线程对象(Thead.currentThread())。sThreadLocal.set(new Looper() ) ;

(2)   Looper.loop()  循环获取messageQueue里的消息

里面有一个死循环for(;;)    去调用   messageQueue.next() 方法获得队头的msg, 然后分发给handler去处理。loop()方法是一个无限循环,之后的代码不会再执行。

(3)Looper.getMainLooper()  获取UI线程的Looper对象

Looper.myLooper()获取当前线程的Looper对象

主线程的looper对象早在ActivityThread启动的时候通过prepareMainLooper()创建好了,因此每次直接获得即可。而获得当前线程的looper对象也很简单, 直接取sThreadLocal存放的looper对象,与prepare()相呼应,sThreadLocal.get()。

3, MessageQueue消息队列

虽然看起来MessageQueue只是一个链表存储的消息容器,但是真正的处理方法都封装在这个类里, 发送消息最终调用的是enqueueMessage(), 轮询消息调用的是next()。这里也巧妙的使用了生产者消费者模式。

enqueueMessage(msg,uptimeMillis)方法 :将消息压入队列,每个消息有时间戳,, msg.when = uptimeMillis;按照uptimeMillis的大小从小到大依次排序插入队列中,uptimeMillis相等的,则先入队列的先处理。

next()方法:获取队列的头部message,即链表的header,值得注意的是,next()方法里也有一个死循环,直到取到满足条件的message才跳出循环,这个条件就是message的执行时间要小于当前时间,message.when < now。如果msg.when <= now, 则不阻塞,直接取出,这不必说, 如果msg.when > now, 就进到下一轮循环里, 直到msg.when <= now.

4, Message消息

一个数据model 本来没什么要说的,但是它有自己的神奇之处。它所能携带的信息自不必说,what唯一标示消息种类,arg1,arg2用于携带简单的数字,obj携带对象类数据, setData(Bundle)携带更复杂的数据。

在Message里有一个实例变量target,target是个handler对象,非常关键, 轮询获取到msg后, msg自调target的dispatchMessage()方法, 继而回调handleMessage()方法处理消息。 这里要注意的是处理消息有一个顺序: 首先msg的callback如有先处理,其次handler创建时带的callback如有则处理,最后才轮到handleMessage()方法处理。

源码结构示意图(可放大查看)

要点:

1,一个线程有且仅有一个Looper和一个MessageQueue!!! 但是可以有多个Handler, 一个msg到底由哪个handler对象来处理,会通过msg.target 来决定

2, Looper.loop()方法一定要在prepare()之后调用,先初始化了才能调,并且 Looper.loop()  之后的代码不能执行,因为是个死循环for(;;) {mQueue.next();  源码可以看到 ActivityThread的main方法里最后一句就是loop()方法; 我们在自己实现子线程的消息通信时,一定要记得这点。(当然HandlerThread推荐给你,本质是一个封装好Looper的Thread子类)

3,MessageQueue里有很多native方法,最主要的两个方法next()和enqueueMessage(),mQueue.next() 方法里也有一个死循环,只有获取到一个有效的msg才跳出循环。

4, Handler构造方法里mLooper = Looper.myLooper();若不指定,则为当前线程, 因为是从ThreadLocal里取出的Looper对象
5,Handler sendMessage→ sendMessageDelayed()→sendMessageAtTime()

每个消息是有时间戳的,postAtFrontOfQueue(),时间戳的值=0,而一般消息的时间戳是当前系统时间,

6,处理消息有一个顺序: 首先msg的callback如有先处理,其次handler创建时带的callback如有则处理,最后才轮到handleMessage()方法处理。 其中post(r), getPostMessage(r)就将r作为msg的callback发出去一个msg。 这里肯定优先执行r,而不是handleMessge()方法。

三, HandlerThread

本质是一个封装好了Looper的Thread子类。HandlerThread extends Thread , 持有一个成员变量mLooper,提供有getLooper()方法,其中在Thread的run() 方法中代()码如下,很简洁,可以看到Looper.loop()方法是最后一行。

@Override
public void run() {   Looper.prepare();    synchronized (this) {       mLooper = Looper.myLooper();        notifyAll();    }       Looper.loop();
}复制代码

使用HandlerThread的方法

   HandlerThread handlerThread = new HandlerThread();        handlerThread.start();Handler handler = new Handler(handlerThread.getLooper()) {@Override   public void handleMessge() { }};handler. sendMessage(msg);
注意: handler的创建必须在HandlerThread.复制代码

Handler的创建必须在handlerThread.start()之后,由源码中可以看到Looper的初始化是在handlerThread的run()方法里!!

转载于:https://juejin.im/post/5cefc6b0e51d45109725fe0b

Android Handler消息机制源码分析相关推荐

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

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

  2. Android 系统(78)---《android framework常用api源码分析》之 app应用安装流程

    <android framework常用api源码分析>之 app应用安装流程 <android framework常用api源码分析>android生态在中国已经发展非常庞大 ...

  3. Android上百实例源码分析以及开源分析集合打包

    感谢网友banketree的收集,压缩包的内容如下: 1.360新版特性界面源代码 实现了360新版特性界面的效果,主要涉及到Qt的一些事件处理与自定义控件.但源码好像是c++. 2.aidl跨进程调 ...

  4. Android主流三方库源码分析(九、深入理解EventBus源码)

    一.EventBus使用流程概念 1.Android事件发布/订阅框架 2.事件传递既可用于Android四大组件间通信 3.EventBus的优点是代码简洁,使用简单,事件发布.订阅充分解耦 4.首 ...

  5. Apache Storm 实时流处理系统通信机制源码分析

    我们今天就来仔细研究一下Apache Storm 2.0.0-SNAPSHOT的通信机制.下面我将从大致思想以及源码分析,然后我们细致分析实时流处理系统中源码通信机制研究. 1. 简介 Worker间 ...

  6. Android 8.0系统源码分析--Camera processCaptureResult结果回传源码分析

    相机,从上到下概览一下,真是太大了,上面的APP->Framework->CameraServer->CameraHAL,HAL进程中Pipeline.接各种算法的Node.再往下的 ...

  7. Android Camera 系统架构源码分析

    Android Camera 系统架构源码分析(1)---->Camera的初始化 Android Camera 系统架构源码分析(2)---->Camera的startPreview和s ...

  8. Android 11.0 Settings源码分析 - 主界面加载

    Android 11.0 Settings源码分析 - 主界面加载 本篇主要记录AndroidR Settings源码主界面加载流程,方便后续工作调试其流程. Settings代码路径: packag ...

  9. Android录音下————AudioRecord源码分析

    Android录音下----AudioRecord源码分析 文章目录 Android录音下----AudioRecord源码分析 一.概述 1.主要分析点 2.储备知识 二.getMinBufferS ...

最新文章

  1. [置顶] WindowsPhone之我见
  2. Learning to rank基本算法小结
  3. 初识linux之给我一个家
  4. CMOS Sensor的调试分享
  5. 前端学习(2617):删除品牌
  6. linux卸载交叉工具,linux-如何从crosstool-ng工具链名称中删除供应...
  7. 满足StrataFlash嵌入式存储器要求的LDO应用电路
  8. 【Flink】基于 Flink CEP 实时计算商品订单流失量
  9. 2014025631嵌入式程序设计第一周学习总结
  10. 云服务器的规格配置该怎么选?
  11. LayoutInflater
  12. html5下移标签,不可不知的html5标签
  13. 品优购项目——黑马程序员pink老师/完整源代码/项目讲解
  14. Bridge的父应用程序不是现用应用程序
  15. CSS实现3D菜单效果【每日一题】
  16. 1.17 “干项目太累,那是因为姿势不对” Stacey矩阵
  17. python爬取可爱女生图片
  18. 推荐 8 个超实用的谷歌 Chrome 插件,大牛都在用
  19. QT自制精美Ui模板系列展示(一)桃子风格模板 - 二次开发专用
  20. Leetcode--Java--340. 至多包含 K 个不同字符的最长子串

热门文章

  1. C#实现二维码功能,winform 以及 asp.net均可以用
  2. 动态绑数据(GridView控件Header和ItemTemplate)
  3. (转)gcc 的简单使用说明
  4. 开源文档管理系统LogicalDOC测试报告---安装篇
  5. javascript判断值是否undefined
  6. 【Spring.net点滴】
  7. Python语言importError:cannot import name ‘InvalidArgumentException‘报错的解决方法:
  8. EXP 导出出错解决方案
  9. 【报告分享】人工智能在五大行业的成就与挑战-毕马威.pdf(附下载链接)
  10. Solving environment: failed conda all InvalidVersionSpecError: Invalid version spec: =2.7报错