Handler 中有四个关键类

  1. Message
  2. Handler
  3. MessageQueue
  4. Looper

介绍一下这四个类的作用

Message

Meaage实现了Parcelable接口,从这里就可以看出,它是一个可序列化的类
在发送消息前,需要把消息事件包装为Message,这里有几个注意事项:
1、这里推荐使用Message.obtain() 来创建Message对象,
2、在每次发送消息前创建Message对象,避免Message对象存在的时间太长,而且在发送Message消息前要使用handler.hasMessages()进行判断,是否已经有此消息,如果有,根据需要进行移除获取return操作。这里的操作都是为了避免出现RuntimeException:This Message is aleady in use的异常。

Handler

用于接收消息,最终处理消息

Handler在构造过程中会确立以下几件事:

  1. 获取当前Handler实例所在线程的Looper对象,mLooper = Looper.myLoopper();
  2. 如果Looper不为空,则获取Looper的消息队列,赋值给Handler的成员变量mQueue:mQueue = mLooper.mQueue
  3. 可以设置CallBack来处理消息回调:mCallback = callback

Handler创建过程可以看出其两个特性:

  1. Handler只能绑定一个线程的Looper;
  2. Handler的消息是发送给Looper的MessageQueue, 需要等待处理;

所以,如果在子线程中声明了一个Handler,是不能直接更新UI的,需要在Handler的构造方法中传入主线程的Looper。
并且要注意,子线程没有默认的Looper,在子线程创建Handler之前,必须先开启子线程的Looper,否则会爆出异常。

  • 发送消息关键方法:
    Handler sendMessage(),最终调用到sendMessageAtTime,调用enqueueMessage,将消息传入MessageQueue,
  • 接收消息关键方法
    Handler dispatchMessage(),如果Message的Callback不为空,则回调到Message的Callback中,如果Message中的Callback为空,则判断Handler中的Callback是否为空,如果不为空,则回调到Handler中的Callback来处理,如果都没有传入Callback,则由handleMessage 处理
    public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

MessageQueue

主要功能有两个,一个插入消息,关键方法enqueueMessage,一个取出消息next,通过单向链表的数据结构来存储消息。下面结合源码进行理解。

boolean enqueueMessage(Message msg, long when) {// 如果msg没有target则抛出异常,target就是Handlerif (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}...//插入消息时,因为是队列,有先后之分,需要同步synchronized (this) {...msg.markInUse();msg.when = when; // when是系统开机时间+延迟时间,得出的消息执行时间Message p = mMessages; // 头部消息boolean needWake;if (p == null || when == 0 || when < p.when) {// p == null 代表当前消息队列中没有消息// when == 0 代表系统刚开机,基本不可能出现// when < p.when 代表此消息的执行时间早于此队列中所有的消息// 将此消息放置在消息队列的头部msg.next = p;mMessages = msg;needWake = mBlocked;} else {              // 插入队列中间。通常我们不必唤醒事件队列,// 除非队列的头部有障碍物,// 并且消息是队列中最早的异步消息。// (p.target == null 则代表他是一个同步屏障,且当前消息为异步消息,异步消息优先)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;}}// 单向链表的插入msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {// 这个方法可以让nativePollOnce立即返回nativeWake(mPtr);}}return true;}
Message next() {// 如果消息循环已退出并已被释放,请返回此处。如果应用程序在退出后尝试重新启动循环器,则可能会发生这种情况,这是不受支持的。// native中MessageQueue的地址值final long ptr = mPtr;if (ptr == 0) {// 如果为0 则说明应用已退出return null;}int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}// 本方法就是执行native消息层// nextPollTimeoutMillis是超时等待时间,如果为-1 则表示无限等待,这个方法会阻塞循环// 如果值为0 ,则无需等待立即返回// 通过Linux的epoll机制进行阻塞nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// Try to retrieve the next message.  Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {// msg.target == null 代表其是屏障消息,则跳过同步消息,寻找下一条异步消息。do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// 比较消息的时间,是不是比当前的时间要大,则需要设置一个延迟时间nextPollTimeoutMillis = (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 (DEBUG) Log.v(TAG, "Returning message: " + msg);msg.markInUse();return msg;}} else {// 没有更多消息时也会进行等待nextPollTimeoutMillis = -1;}// 下面是进行一些idleHandler的处理// 此处暂时不展开了...}...}}

Looper

Looper是自从启动后就开始无限循环,不断的从消息队列中获取消息,然后分发处理事件,APP启动时,在ActivityThread的main()方法中已经启动了Looper循环,关键方法 loop();一下是源码中的loop方法:

/**
119     * Run the message queue in this thread. Be sure to call
120     * {@link #quit()} to end the loop.
121     */
122    public static void loop() {123        final Looper me = myLooper();
124        if (me == null) {125            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
126        }
127        final MessageQueue queue = me.mQueue;
128
129        // Make sure the identity of this thread is that of the local process,
130        // and keep track of what that identity token actually is.
131        Binder.clearCallingIdentity();
132        final long ident = Binder.clearCallingIdentity();
133
134        for (;;) {135            Message msg = queue.next(); // might block
136            if (msg == null) {137                // No message indicates that the message queue is quitting.
138                return;
139            }
140
141            // This must be in a local variable, in case a UI event sets the logger
142            Printer logging = me.mLogging;
143            if (logging != null) {144                logging.println(">>>>> Dispatching to " + msg.target + " " +
145                        msg.callback + ": " + msg.what);
146            }
147
148            msg.target.dispatchMessage(msg);
149
150            if (logging != null) {151                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
152            }
153
154            // Make sure that during the course of dispatching the
155            // identity of the thread wasn't corrupted.
156            final long newIdent = Binder.clearCallingIdentity();
157            if (ident != newIdent) {158                Log.wtf(TAG, "Thread identity changed from 0x"
159                        + Long.toHexString(ident) + " to 0x"
160                        + Long.toHexString(newIdent) + " while dispatching to "
161                        + msg.target.getClass().getName() + " "
162                        + msg.callback + " what=" + msg.what);
163            }
164
165            msg.recycleUnchecked();
166        }
167    }

重点流程
1、获取Looper对象
2、获取MessageQueue对象
3、死循环遍历
4、通过queue.next获取一条Message
5、通过msg.target.dispatchMessage(msg) 分发Message
6、通过msg.recycleUnchecked() 回收Message到消息池中

Android Hanlder的理解相关推荐

  1. Android Activity的理解

    Android Activity的理解 Activity 生命周期的四个状态 Activity的生命周期分为运行.暂停.停止.销毁四个状态. 运行状态:该Activity生命开始,Activity在前 ...

  2. android 背光灯分析,Android灯光系统--深入理解背光灯

    Android灯光系统--深入理解背光灯 一.怎么控制背光灯(简述) APP将亮度值写入数据库 线程检测数据库的值是否发生变化 这种机制成为"内容观察者"--contentObse ...

  3. android handler的理解

    android handler的理解 在看handler源码前,我一直以为google构造handler的目的是方便开发者在其他线程中 调用执行主线程的方法或者在主线程中调用执行其他线程的方法.看完源 ...

  4. Android系统分区理解及目录细解

    Android系统分区 分区种类 Android 通常有以下分区: System分区: 就是我们刷ROM的分区 Data分区:   分区就是我们装APK的分区 Catch分区:是缓存分区 SDCard ...

  5. Android回调函数理解

    Android回调函数理解,比如我用一个activity去做显示下载进度的一个进度条,但是下载是另外一个B类来做的,这个时候我Activity获取下载的进度就可以提供一个回调接口,然后让下载类来回调就 ...

  6. 谈谈你对Android NDK的理解

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言   6月 26 日, Google Android 发布了 NDK ,引起了很多发人员的兴趣. NDK 全称: Na ...

  7. Android进阶笔记07:Android之MVC 理解

     1. 为什么需要MVC ? 软件中最核心的,最基本的东西是什么?  答:是的,是数据.我们写的所有代码,都是围绕数据的.      围绕着数据的产生.修改等变化,出现了业务逻辑.      围绕着数 ...

  8. Android adt 初步理解和分析(三)

    前面已经安装好了sdk,虽然只是下载了一个最早的1.5,但至少有一个sdk了,下面就要用模拟器来运行这个sdk里面的镜像文件了. 打开 android virtual device manager 工 ...

  9. android ipc简单理解,Android IPC 机制【1】--简介

    一.android 中进程间通信常用的有以下几种机制 ------------------------------------------------------------------------- ...

最新文章

  1. ICRA 2021 | VINS 研讨会概要(附完整视频)
  2. 如何利用扬声器构建深度学习网络?
  3. spring bean生命周期管理--转
  4. 微信研究员解析深度学习在NLP中的发展和应用
  5. Kettle7.1在window启动报错
  6. c语言中加法和乘法的消耗,急!!!!c语言:求n次多项式的加法和乘法
  7. String s = new String(“hello“)创建了几个对象(图解)
  8. android动态波浪效果,android贝塞尔曲线实现波浪效果
  9. 安装vmware-tools遇the path is not valid path to the gcc binary和the path is not a valid path to th...
  10. linux sata硬盘热交换,学员原创-杨欢最详细西数硬盘热交换流程
  11. python找重复元素_Python笔记(二)查找重复元素
  12. php处理excel里面的重复数据,Excel导入时,跳过重复数据,并提示重复数据数量与明细...
  13. 基于Android的个人时间管理设计与开发
  14. python-重难点知识汇总
  15. php rewind函数,函数rewind的作用是什么
  16. 陈老师的一些单片机外围电路设计心得
  17. 人机交互设备(HID)
  18. TextWatcher初识
  19. 远程桌面连接后远程计算机注销,windows2003远程桌面退出后系统自动注销的问题...
  20. Rhinoceros 建模简介1

热门文章

  1. 配置MSTP功能示例
  2. C++ 小游戏 视频及资料集(四)
  3. 抖音自主品牌号该发什么作品
  4. Java 8除不尽的数
  5. 【百度快照】基于MATLAB GUI的条形码识别系统
  6. 对PhD一年级新生有什么建议?
  7. Debian10更换软件源
  8. redis连接超时,本地连接不上服务器上的redis
  9. Chrome插件 Tamper Dev
  10. 贪心算法(Greedy Algorithms)