Android消息机制 Looper源码阅读
什么是Looper
android源码上Looper类的注释为:
- 用于为线程运行消息循环的类,在默认情况下线程是没有与其关联的消息循环的;可以通过在线程中调用Looper.prepare()创建一个与线程绑定的消息循环,让其处理消息,直到循环停止。
- 大多数情况下与Looper(消息循环) 的交互都是通过 Handler类进行的
- 下述代码展示了一个典型的线程创建Looper的实例:
class LooperThread extends Thread {public Handler mHandler;public void run() {Looper.prepare();mHandler = new Handler() {public void handleMessage(Message msg) {//处理传入进来的消息}}Looper.loop();}
}
复制代码
Looper作用
- 此类用于准备线程的消息循环类(prepare(boolean quitAllowed)) 、开始消息循环(loop())、消息循环的退出(quit() /quitSafely())。
Looper源码解析
在Looper作用中提到了其三个作用和对应的方法,下面分别来看这些方法的实现:
prepare()
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) );}
复制代码
上述代码调用了 ThreadLocal.set() 保存了Looper实例
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}
复制代码
Looper的构造方法中对MessageQueue和Thread进行了实例化。
loop()
/*** Run the message queue in this thread. Be sure to call* {@link #quit()} to end the 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;for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}try {msg.target.dispatchMessage(msg); } msg.recycleUnchecked();}}
复制代码
上述代码是经过删减留下的主要逻辑代码,其中可以看到:
- 获取当前的Looper实例进行非空判断。
- 获取当前Looper中的MessageQueue。
- 通过一个死循环调用queue.next方法取出Message
- 当Message为空时表明MessageQueue正在退出,则跳出循环
- 否则继续往下通过Message.target获取Handler,调用其dispatchMessage将Message分发给Handler处理
- 最后调用Message的 recycleUnchecked() 方法将其内容清除并放入消息池中循环使用(节省了Message创建以及销毁的时间,并提高了内存的使用率);
quit() /quitSafely()
/*** Quits the looper.* <p>* Causes the {@link #loop} method to terminate without processing any* more messages in the message queue.* </p><p>* Any attempt to post messages to the queue after the looper is asked to quit will fail.* For example, the {@link Handler#sendMessage(Message)} method will return false.* </p><p class="note">* Using this method may be unsafe because some messages may not be delivered* before the looper terminates. Consider using {@link #quitSafely} instead to ensure* that all pending work is completed in an orderly manner.* </p>** @see #quitSafely*/public void quit() {mQueue.quit(false);}
复制代码
/*** Quits the looper safely.* <p>* Causes the {@link #loop} method to terminate as soon as all remaining messages* in the message queue that are already due to be delivered have been handled.* However pending delayed messages with due times in the future will not be* delivered before the loop terminates.* </p><p>* Any attempt to post messages to the queue after the looper is asked to quit will fail.* For example, the {@link Handler#sendMessage(Message)} method will return false.* </p>*/public void quitSafely() {mQueue.quit(true);}
复制代码
上述quit()方法会将消息队列中的所有消息移除;而quitSafely()则是将消息队列中所有的延迟消息移除,及时消息继续处理结束后再退出。上述两个方法在调用过后,再调用Handler的sendMessage则会返回false,也就是不能再发送消息了。
myLooper()
/*** Return the Looper object associated with the current thread. Returns* null if the calling thread is not associated with a Looper.*/public static @Nullable Looper myLooper() {return sThreadLocal.get();}
复制代码
上述方法通过ThreadLocal获取当前线程的Looper。
ThreadLocal
在perpare() 中我们看到调用了ThreadLocal的set方法保存了当前线程的Looper实例,下面展示set的代码
/*** Sets the current thread`s copy of this thread-local variable* to the specified value. Most subclasses will have no need to* override this method, relying solely on the {@link #initialValue}* method to set the values of thread-locals.** @param value the value to be stored in the current thread`s copy of* this thread-local.*/public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}
复制代码
可以看出会先获取当前线程的ThreadLocalMap(此类为ThreadLocal的静态内部类),然后根据判断结果通过不同的方法保存value值;下面展示ThreadLocalMap的set方法
/*** 该数组根据需要调整大小.* 该数组长度总是为2的幂数.*/private Entry[] table;private void set(ThreadLocal<?> key, Object value) {// We don`t use a fast path as with get() because it is at// least as common to use set() to create new entries as// it is to replace existing ones, in which case, a fast// path would fail more often than not.Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();}
复制代码
- 首先循环Entry数组(Entry为继承弱引用的类)
- 判断是否有与key相等的值,若有则替换value值并返回;
- 判断是k值是非为空,若为空则将其替换为key并返回
- new个新的Entry值赋给tab[i]
- 上述Entry结构为
static class Entry extends WeakReference<ThreadLocal<?>> {/*** The value associated with this ThreadLocal.*/Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}
复制代码
在myLooper() 中我们看到调用了ThreadLocal的get方法获取了当前线程的Looper实例,下面展示get的代码
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T) e.value;return result;}}return setInitialValue();}
复制代码
可以看出与set()方法类似首先会取出当前线程的ThreadLocalMap,然后将匹配的Entry取出,通过之前代码我们知道Looper的实例赋值给了value的,因此e.value的值即为当前线程的Looper。
总结
- 阅读Looper源码得知,我们可以通过Looper与Handler的组合让线程一直保持着待命的状态,当有消息时能及时处理消息。
- looper是存储在当前线程的ThreadLocalMap中的,ThreadLocal的get和set方法都是操作当前线程的ThreadLocalMap的,因此在不同的线程中调用ThreadLocal的get和set方法操作的对象是不同的。
转载于:https://juejin.im/post/5cfe14746fb9a07eab687564
Android消息机制 Looper源码阅读相关推荐
- 什么是对象的消息_这一次,我们用最详细的方式解析Android消息机制的源码
Handler源码解析 一.创建Handler对象 使用handler最简单的方式:直接new一个Handler的对象 Handler handler = new Handler(); 所以我们来看看 ...
- 我们用最详细的方式解析Android消息机制的源码,含小米、腾讯、阿里
历时半年,我们终于整理出了这份市面上最全面的最新Android面试题解析大全! 章节目录 第一章:Android 基础 面试题 第二章:Android 高级 面试题 第三章:开源框架实战面试解析 第四 ...
- 我们用最详细的方式解析Android消息机制的源码,经典好文
有人问,为什么想要投递大厂,总结一下大概有这么几个出发点: 1.追求高薪资 相对小厂而言,大厂的薪资水平会更高不少.具体数字区间就不细说了,但是大厂整体会大方很多,只要你能够通过面试,30%甚至更高的 ...
- 我们用最详细的方式解析Android消息机制的源码,小白也能看明白
前言 程序员说不焦虑其实是假的,因为无论是现在还是最近几年,很早就有人察觉Android开发的野蛮生长时代已经过去.过去的优势是市场需要,这个技术少有人有,所以在抢占市场的时候,基本上满足需要就已经可 ...
- android消息机制—Looper
Looper在android的消息机制充当消息循环的角色,它不停的充MessageQueue中拿出消息,并将消息交给Hanlder处理,下面是他的常用方法解析. 项目源码 Looper的创建 1. a ...
- Android Binder机制情景源码分析之Binder回调注册和反注册
我们在日常开发中,经常用到Binder来进行跨进程通信,有个比较常见的场景是向服务端注册Binder回调,比如: IActivityManager中有两个成对的方法,Client端向AMS所在的服务端 ...
- java jdk 类加载机制_JDK源码阅读之类加载
java类加载 类的生命周期(类加载过程) LLIUU+VPR 加载(Loading) 链接(Linking) 验证(Verification) 准备(Preparation) 解析(Resoluti ...
- android handler的机制和原理_一文搞懂handler:彻底明白Android消息机制的原理及源码
提起Android消息机制,想必都不陌生.其中包含三个部分:Handler,MessageQueue以及Looper,三者共同协作,完成消息机制的运行.本篇文章将由浅入深解析Android消息机制的运 ...
- Android消息机制详解
*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 Android消息机制 这一定是一个被写烂了的专题吧.那本媛也来凑一个热闹吧.哈哈 这篇博客将会涉及以下内容: 消息机制概述 UM ...
最新文章
- Mysql4种方式避免重复插入数据!
- qgis经纬度_数据养成系列--QGIS地理空间
- jenkins 漏洞集合 简介
- 运算符优先级和结合性
- 【渝粤教育】国家开放大学2018年秋季 0734-22T出纳实务 参考试题
- idea新建web工程
- 达摩院发布:2022年十大科技趋势
- 2020计算机视觉会议地点,2020年计算机视觉与信息技术国际会议(CVIT 2020)
- 基于pg_qualstats和hypopg的自动索引调优
- Loom插件怎么用?Loom录屏插件使用教程
- 强网杯-强网先锋辅助
- 【wsl2】从头开始配置
- 计算机电池维修心得,CMOS电池引起计算机无法启动的检修详解
- 语义分割yolov5 v4.0 baseline快速复现 快速跑通 图像分割 图像分类 重新训练,训练结果,测试结果,实验图片-20210227
- UNIX网络编程:卷2-读书笔记
- 关于如何使用打码平台识别验证码
- 人脸识别测温一体道闸机省时更省力
- 计算机图形学——DDA算法
- 阿里悄悄启动教育新基建
- linux下arp攻击的解决方案[原]
热门文章
- pyCharm编辑器激活使用
- 2017年4月25日(日志库glog)
- 33.Linux系统介绍
- Android打Path的方法
- OpenGL ES 详解纹理生成和纹理映射步骤以及函数
- MySQL 超时解决方案mysql报错处理: could not be resolved: getnameinfo() returned error (code: -3)...
- 阿里云 Aliplayer高级功能介绍(三):多字幕
- 头脑风暴-如何减少软件项目对于人的依赖性。
- MySQL事务隔离级别介绍
- ios系统 ipa文件 打包流程详解 及 常见问题处理