第10章 Android的消息机制

10.1 Android消息机制概述

(1)Android的消息机制主要是指Handler的运行机制,其底层需要MessageQueueLooper的支撑。MessageQueue是以单链表的数据结构存储消息列表但是以队列的形式对外提供插入和删除消息操作的消息队列。MessageQueue只是消息的存储单元,而Looper则是以无限循环的形式去查找是否有新消息,如果有的话就去处理消息,否则就一直等待着。
(2)Handler的主要作用是将一个任务切换到某个指定的线程中去执行。
为什么要提供这个功能呢?
Android规定UI操作只能在主线程中进行,ViewRootImplcheckThread方法会验证当前线程是否可以进行UI操作。
为什么不允许子线程访问UI呢?
这是因为UI组件不是线程安全的,如果在多线程中并发访问可能会导致UI组件处于不可预期的状态。另外,如果对UI组件的访问进行加锁机制的话又会降低UI访问的效率,所以还是采用单线程模型来处理UI事件。
(3)Handler的创建会采用当前线程的Looper来构建内部的消息循环系统,如果当前线程中不存在Looper的话就会报错。Handler可以用post方法将一个Runnable投递到消息队列中,也可以用send方法发送一个消息投递到消息队列中,其实post最终还是调用了send方法。

10.2 Android的消息机制分析

(1)ThreadLocal的工作原理
1.ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,可以考虑使用ThreadLocal。 对于Handler来说,它需要获取当前线程的Looper,而Looper的作用域就是线程并且不同线程具有不同的Looper,这个时候通过ThreadLocal就可以实现Looper在线程中的存取了。
2.ThreadLocal的原理:不同线程访问同一个ThreadLocal的get方法时,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找出对应的value值,不同线程中的数组是不同的,这就是为什么通过ThreadLocal可以在不同线程中维护一套数据的副本并且彼此互不干扰。
3.ThreadLocal是一个泛型类public class ThreadLocal<T>,下面是它的set方法

public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value);}

Values是Thread类内部专门用来存储线程的ThreadLocal数据的,它内部有一个数组private Object[] table,ThreadLocal的值就存在这个table数组中。如果values的值为null,那么就需要对其进行初始化然后再将ThreadLocal的值进行存储。
ThreadLocal数据的存储规则:ThreadLocal的值在table数组中的存储位置总是ThreadLocal的索引+1的位置。

(2)MessageQueue的工作原理
1.MessageQueue其实是通过单链表来维护消息列表的,它包含两个主要操作enqueueMessagenext,前者是插入消息,后者是取出一条消息并移除。
2.next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将它从链表中移除。

(3)Looper的工作原理
1.为一个线程创建Looper的方法,代码如下所示

new Thread("test"){    @Override public void run() { Looper.prepare();//创建looper Handler handler = new Handler();//可以创建handler了 Looper.loop();//开始looper循环 }}.start();

2.Looper的prepareMainLooper方法主要是给主线程也就是ActivityThread创建Looper使用的,本质也是调用了prepare方法。
3.Looper的quitquitSafely方法的区别是:前者会直接退出Looper,后者只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全地退出。Looper退出之后,通过Handler发送的消息就会失败,这个时候Handler的send方法会返回false。
在子线程中,如果手动为其创建了Looper,那么在所有的事情完成以后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于等待的状态,而如果退出Looper以后,这个线程就会立刻终止,因此建议不需要的时候终止Looper。
4.Looper的loop方法会调用MessageQueuenext方法来获取新消息,而next是一个阻塞操作,当没有消息时,next方法会一直阻塞着在那里,这也导致了loop方法一直阻塞在那里。如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg),其中的msg.target就是发送这条消息的Handler对象。

(4)Handler的工作原理
1.Handler就是处理消息的发送和接收之后的处理;
2.Handler处理消息的过程

public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg);//当message是runnable的情况,也就是Handler的post方法传递的参数,这种情况下直接执行runnable的run方法 } else { if (mCallback != null) {//如果创建Handler的时候是给Handler设置了Callback接口的实现,那么此时调用该实现的handleMessage方法 if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg);//如果是派生Handler的子类,就要重写handleMessage方法,那么此时就是调用子类实现的handleMessage方法 }}

private static void handleCallback(Message message) { message.callback.run();}

/** * Subclasses must implement this to receive messages. */public void handleMessage(Message msg) {}

3.Handler还有一个特殊的构造方法,它可以通过特定的Looper来创建Handler。

public Handler(Looper looper){ this(looper, null, false);}

4.Android的主线程就是ActivityThread,主线程的入口方法就是main,其中调用了Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()方法来开启主线程的消息循环。主线程内有一个Handler,即ActivityThread.H,它定义了一组消息类型,主要包含了四大组件的启动和停止等过程,例如LAUNCH_ACTIVITY等。
ActivityThread通过ApplicationThreadAMS进行进程间通信,AMS以进程间通信的方法完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型。

OK,本章结束,谢谢阅读。

转载于:https://www.cnblogs.com/dongweiq/p/5029036.html

《Android开发艺术探索》读书笔记 (10) 第10章 Android的消息机制相关推荐

  1. Android开发艺术探索 读书笔记

    啥也不说了,@主席的<Android开发艺术探索>真是业界良心之作,不得不看!感谢主席,膜拜主席!主席主席,我要跟你生猴子!(>^ω^<) 读书笔记中若有任何问题请留言告知,谢 ...

  2. Android开发艺术探索读书笔记(一)

    首先向各位严重推荐主席这本书<Android开发艺术探索>. 再感谢主席邀请写这篇读书笔记 + 书评.书已经完整的翻完一遍了,但是还没有细致的品读并run代码,最近有时间正好系统的把整本书 ...

  3. Android开发艺术探索读书笔记

    前言 Android开发艺术(这本书真的是艺术,太崇拜刚哥了,值得每一个做Android开发刷十遍的书) 1,Activity生命周期和启动模式 典型情况下的生命周期分析 onCreate() onS ...

  4. Android 开发艺术探索 - 读书笔记目录

    仅作为读书笔记使用,建议阅读原书. 书中代码部分已和现版本不符,建议对比最新版本学习. 读了这本书,越发认识到和大佬们的差距.嗯,加油吧. 过去の自分が今仆の土台となる 第 1 章 - Activit ...

  5. Android开发艺术探索读书笔记(二)

    首先感谢大家支持,昨天第一篇写出来之后反响很好,主席本人也非常赞赏(捂脸-),再接再厉,推出第二篇.这篇的主要内容是对两章View的内容进行总结.不得不说,自定义View是很多开发者的痛点,一方面我们 ...

  6. Android开发艺术探索读书笔记(第5章 RemoteView)

    原理 RemoteView的作用是在其他进程中显示并更新view界面. 大量的IPC操作会影响效率,为了解决这个问题,系统并没有通过Binder去直接支持View的跨进程访问,而是提供了一个Actio ...

  7. 《android开发艺术探索》笔记之Bitmap的加载和Cache

    <Android开发艺术探索>笔记之Bitmap的加载和Cache<一> 我放暑假前,就在图书馆借了一本<Android开发艺术探索>,这也是我看到很多人推荐的.之 ...

  8. 《Android 开发艺术探索》笔记2--IPC机制

    <Android 开发艺术探索>笔记2--IPC机制 思维导图 Android IPC简介 Android中的多进程的模式 IPC基础概念 Serializable接口 Parcelabl ...

  9. 《Android开发艺术探索》笔记目录

    该笔记以<Android开发艺术探索>为基础,结合Android 9.0代码和官方文档,修正了原书中表述不明确和过时的部分,同时加入了大量的个人理解. 13章,14章,15章是总结性的章节 ...

  10. Android开发艺术探索学习笔记 第二章IPC

    最近将之前工作做本地的学习笔记上传一下 这里是Android艺术开发探索的前三章内容 文章目录 1. android的多进程模式 2. IPC基础概念介绍 2.1 Serializable 2.2Pa ...

最新文章

  1. Axis2 -POJO
  2. 全球及中国绿色建筑产业规模现状与未来走势分析报告2022版
  3. 总结 XSS 与 CSRF 两种跨站攻击
  4. ArcGIS10.8中如何获取线状、面状数据的折点,并计算折点坐标?
  5. 链表之删除单链表倒数第K个节点
  6. 【转载】使用Imaging组件加载GIF动画
  7. SVN使用过程中出现“工作副本已经锁定”的解决办法
  8. Lenovo Quick Fix:在Win7系统镜像中注入USB3.0和NVMe驱动解决无法安装Win7的问题
  9. idea各工作区背景颜色设置
  10. visio软件接口流程图_用Visio画流程图
  11. Windows显示文件名后缀的方法
  12. 英伟达辟谣 RTX 3060 被破解传闻
  13. 『每日AI』马化腾丨中国互联网已从C2C进化为KFC!
  14. [转贴]金庸的九家著名公司
  15. 2、OpencvSharp 读取图片
  16. 用HTML写一首诗并配上图片,需要满足诗的格式
  17. 怎么查看html页面,网页浏览记录如何查看_怎样查历史网页浏览记录-win7之家
  18. 中国国内采购平台大全
  19. 关于LD1117S12TR
  20. 姜奇平:去伪存真看云计算

热门文章

  1. 2020暨阳学院园林计算机考研考场,【图片】2020考研,老学长教你如何规划!【计算机考研吧】_百度贴吧...
  2. java底层 文件操作_JAVA的文件操作【转】
  3. redhat linux7.0的安装
  4. expected initializer before
  5. 异步日志系统设计demo
  6. 《MySQL——order by逻辑(全字段排序与rowid排序)》
  7. C++语法:vector的使用
  8. python 抠图 锯齿_Python | 绘图中的抗锯齿
  9. 顺序表(代码、分析、汇编)
  10. leetcode内存消耗