Android Hanlder的理解
Handler 中有四个关键类
- Message
- Handler
- MessageQueue
- 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在构造过程中会确立以下几件事:
- 获取当前Handler实例所在线程的Looper对象,mLooper = Looper.myLoopper();
- 如果Looper不为空,则获取Looper的消息队列,赋值给Handler的成员变量mQueue:mQueue = mLooper.mQueue
- 可以设置CallBack来处理消息回调:mCallback = callback
Handler创建过程可以看出其两个特性:
- Handler只能绑定一个线程的Looper;
- 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的理解相关推荐
- Android Activity的理解
Android Activity的理解 Activity 生命周期的四个状态 Activity的生命周期分为运行.暂停.停止.销毁四个状态. 运行状态:该Activity生命开始,Activity在前 ...
- android 背光灯分析,Android灯光系统--深入理解背光灯
Android灯光系统--深入理解背光灯 一.怎么控制背光灯(简述) APP将亮度值写入数据库 线程检测数据库的值是否发生变化 这种机制成为"内容观察者"--contentObse ...
- android handler的理解
android handler的理解 在看handler源码前,我一直以为google构造handler的目的是方便开发者在其他线程中 调用执行主线程的方法或者在主线程中调用执行其他线程的方法.看完源 ...
- Android系统分区理解及目录细解
Android系统分区 分区种类 Android 通常有以下分区: System分区: 就是我们刷ROM的分区 Data分区: 分区就是我们装APK的分区 Catch分区:是缓存分区 SDCard ...
- Android回调函数理解
Android回调函数理解,比如我用一个activity去做显示下载进度的一个进度条,但是下载是另外一个B类来做的,这个时候我Activity获取下载的进度就可以提供一个回调接口,然后让下载类来回调就 ...
- 谈谈你对Android NDK的理解
2019独角兽企业重金招聘Python工程师标准>>> 1.前言 6月 26 日, Google Android 发布了 NDK ,引起了很多发人员的兴趣. NDK 全称: Na ...
- Android进阶笔记07:Android之MVC 理解
1. 为什么需要MVC ? 软件中最核心的,最基本的东西是什么? 答:是的,是数据.我们写的所有代码,都是围绕数据的. 围绕着数据的产生.修改等变化,出现了业务逻辑. 围绕着数 ...
- Android adt 初步理解和分析(三)
前面已经安装好了sdk,虽然只是下载了一个最早的1.5,但至少有一个sdk了,下面就要用模拟器来运行这个sdk里面的镜像文件了. 打开 android virtual device manager 工 ...
- android ipc简单理解,Android IPC 机制【1】--简介
一.android 中进程间通信常用的有以下几种机制 ------------------------------------------------------------------------- ...
最新文章
- ICRA 2021 | VINS 研讨会概要(附完整视频)
- 如何利用扬声器构建深度学习网络?
- spring bean生命周期管理--转
- 微信研究员解析深度学习在NLP中的发展和应用
- Kettle7.1在window启动报错
- c语言中加法和乘法的消耗,急!!!!c语言:求n次多项式的加法和乘法
- String s = new String(“hello“)创建了几个对象(图解)
- android动态波浪效果,android贝塞尔曲线实现波浪效果
- 安装vmware-tools遇the path is not valid path to the gcc binary和the path is not a valid path to th...
- linux sata硬盘热交换,学员原创-杨欢最详细西数硬盘热交换流程
- python找重复元素_Python笔记(二)查找重复元素
- php处理excel里面的重复数据,Excel导入时,跳过重复数据,并提示重复数据数量与明细...
- 基于Android的个人时间管理设计与开发
- python-重难点知识汇总
- php rewind函数,函数rewind的作用是什么
- 陈老师的一些单片机外围电路设计心得
- 人机交互设备(HID)
- TextWatcher初识
- 远程桌面连接后远程计算机注销,windows2003远程桌面退出后系统自动注销的问题...
- Rhinoceros 建模简介1