什么是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作用中提到了其三个作用和对应的方法,下面分别来看这些方法的实现:

  1. 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进行了实例化。

  1. 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创建以及销毁的时间,并提高了内存的使用率);
  1. 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,也就是不能再发送消息了。

  1. 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。

  1. 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源码阅读相关推荐

  1. 什么是对象的消息_这一次,我们用最详细的方式解析Android消息机制的源码

    Handler源码解析 一.创建Handler对象 使用handler最简单的方式:直接new一个Handler的对象 Handler handler = new Handler(); 所以我们来看看 ...

  2. 我们用最详细的方式解析Android消息机制的源码,含小米、腾讯、阿里

    历时半年,我们终于整理出了这份市面上最全面的最新Android面试题解析大全! 章节目录 第一章:Android 基础 面试题 第二章:Android 高级 面试题 第三章:开源框架实战面试解析 第四 ...

  3. 我们用最详细的方式解析Android消息机制的源码,经典好文

    有人问,为什么想要投递大厂,总结一下大概有这么几个出发点: 1.追求高薪资 相对小厂而言,大厂的薪资水平会更高不少.具体数字区间就不细说了,但是大厂整体会大方很多,只要你能够通过面试,30%甚至更高的 ...

  4. 我们用最详细的方式解析Android消息机制的源码,小白也能看明白

    前言 程序员说不焦虑其实是假的,因为无论是现在还是最近几年,很早就有人察觉Android开发的野蛮生长时代已经过去.过去的优势是市场需要,这个技术少有人有,所以在抢占市场的时候,基本上满足需要就已经可 ...

  5. android消息机制—Looper

    Looper在android的消息机制充当消息循环的角色,它不停的充MessageQueue中拿出消息,并将消息交给Hanlder处理,下面是他的常用方法解析. 项目源码 Looper的创建 1. a ...

  6. Android Binder机制情景源码分析之Binder回调注册和反注册

    我们在日常开发中,经常用到Binder来进行跨进程通信,有个比较常见的场景是向服务端注册Binder回调,比如: IActivityManager中有两个成对的方法,Client端向AMS所在的服务端 ...

  7. java jdk 类加载机制_JDK源码阅读之类加载

    java类加载 类的生命周期(类加载过程) LLIUU+VPR 加载(Loading) 链接(Linking) 验证(Verification) 准备(Preparation) 解析(Resoluti ...

  8. android handler的机制和原理_一文搞懂handler:彻底明白Android消息机制的原理及源码

    提起Android消息机制,想必都不陌生.其中包含三个部分:Handler,MessageQueue以及Looper,三者共同协作,完成消息机制的运行.本篇文章将由浅入深解析Android消息机制的运 ...

  9. Android消息机制详解

    *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 Android消息机制 这一定是一个被写烂了的专题吧.那本媛也来凑一个热闹吧.哈哈 这篇博客将会涉及以下内容: 消息机制概述 UM ...

最新文章

  1. Mysql4种方式避免重复插入数据!
  2. qgis经纬度_数据养成系列--QGIS地理空间
  3. jenkins 漏洞集合 简介
  4. 运算符优先级和结合性
  5. 【渝粤教育】国家开放大学2018年秋季 0734-22T出纳实务 参考试题
  6. idea新建web工程
  7. 达摩院发布:2022年十大科技趋势
  8. 2020计算机视觉会议地点,2020年计算机视觉与信息技术国际会议(CVIT 2020)
  9. 基于pg_qualstats和hypopg的自动索引调优
  10. Loom插件怎么用?Loom录屏插件使用教程
  11. 强网杯-强网先锋辅助
  12. 【wsl2】从头开始配置
  13. 计算机电池维修心得,CMOS电池引起计算机无法启动的检修详解
  14. 语义分割yolov5 v4.0 baseline快速复现 快速跑通 图像分割 图像分类 重新训练,训练结果,测试结果,实验图片-20210227
  15. UNIX网络编程:卷2-读书笔记
  16. 关于如何使用打码平台识别验证码
  17. 人脸识别测温一体道闸机省时更省力
  18. 计算机图形学——DDA算法
  19. 阿里悄悄启动教育新基建
  20. linux下arp攻击的解决方案[原]

热门文章

  1. pyCharm编辑器激活使用
  2. 2017年4月25日(日志库glog)
  3. 33.Linux系统介绍
  4. Android打Path的方法
  5. OpenGL ES 详解纹理生成和纹理映射步骤以及函数
  6. MySQL 超时解决方案mysql报错处理: could not be resolved: getnameinfo() returned error (code: -3)...
  7. 阿里云 Aliplayer高级功能介绍(三):多字幕
  8. 头脑风暴-如何减少软件项目对于人的依赖性。
  9. MySQL事务隔离级别介绍
  10. ios系统 ipa文件 打包流程详解 及 常见问题处理