• Android-消息机制总结


    引子

    项目进度慢下来,终于有时间可以温故知新。

    消息机制,贯穿Android系统的 事件传递机制,包含1个主角Handler,3个配角 ThreadLocal,MessageQueue,Looper,本文将详述4个东西之间的关系,大概这就是消息机制的原理了吧。

     

    关键词


    Handler,ActivityThread,ThreadLocal,MessageQueue,Looper ···

     

    主角 - Handler


    正确的说法是:Handler的作用,是切换任务执行的线程 (理论上,可以切换到任意线程,比如,切到子线程)。

    而由于android更新UI只能在主线程,所以,我们平时用handler就是通知主线程更新UI,如果强行让子线程去更新UI,则会报出异常:Only the original thrad that created a view hierarchy can touch its view(只有创建View的线程可以解除它的view。主线程创建View)

    事实上,主线程,子线程,在本质上都一样,只是android里面对主线程做了详细设定,让它只能更新UI,不能进行耗时操作(如果耗时操作持续一定时间,大概5秒,系统就会判定程序已经死了,报出ANR异常)。

    发散一下:

    1、为何谷歌不让子线程去更新UI,因为android的UI控件并不是线程安全的,这就意味着,并发操作将会引发不可控的结果。

    2、为何不把控件做成线程安全的呢?因为要做做成线程安全,就要对更新控件的函数进行加锁,加锁有缺点,一是让UI访问变得复杂,二是,降低UI的访问效率。所以,与其加锁,还不如干脆让 默认的一个主线程来执行所有的UI操作。

     

    配角1号 ThreadLocal

    乍一看,这个难道是一个线程的派生类?NO,它和线程有关,但是并不是线程。

    ThreadLocal的作用是:以线程对象为作用域,进行数据存储

    什么意思? 就是,它类似(但是并不等同)一个Map映射,以线程对象为key,来进行 数据存储。

    举个例子: 如果你有个需求,同时开启多个线程进行文件下载,而你需要在每个线程中都存一个独立的“下载进度”,要保证他们互不干涉。这个就符合ThreadLocal的使用场景,直接使用它就行了,当然你也可以用其他方式,这里只是举例说明。

    Android中消息机制,ThreadLocal被用来 存Looper对象,让每个线程的Looper对象相互独立

     

    想看源码的,请移步Looper.java,搜索ThreadLocal...

     

     

    ThreadLocal构造函数中有一个泛型,这里指定了Looper作为存储类型

     

     配角2号 MessageQueue

    MessageQueue,消息的容器。

    看上去是一个Queue,队列?NO,谷歌的大佬们也会玩障眼法。其实,MessageQueue的数据结构是:单链表,为什么用单链表?因为单链表在 数据的插入和删除方面要优于队列。

    直接找到MessageQueue.java 源码,可以找到它的2个关键方法:

    1、enqueueMessage() 消息入列

    参数1,就是msg消息对象。

    它的作用,就是将msg对象插入到单链表中。

    2、next()获取消息

    返回值是消息对象。

    分析源码得出,

    它是用一个无限for循环 ,获取一个Message,如果获取不到,就会一直循环,阻塞在这里,直到有 消息,就把消息返回出去,并且把这个消息从单链表中删除。

     

    配角3号 Looper

    Looper 消息循环

    关键方法:

     

    用于当前线程中创建一个Looper对象,这是个静态方法,只需要调用Looper.prepare().它会自动创建本线程的Looper对象,然而,最多创建一个,多次创建会报异常。

    用于启动当前线程Looper的消息循环(进入源码能看到,没错,又是无限for循环···)

    无限循环,检查MessageQueue中有没有新的消息,如果有,就调用msg.target.dispatchMessage(msg),如果没有,就被queue.next()阻塞了。除非next返回null(什么时候会返回空?结论是消息队列被标记为推出状态时,会返回空)

    角色关系

    OK,角色介绍完了,那么他们之间怎么发生作用,共同构成Android的消息机制呢?

    看图;

    图画得吃藕,不要在意这些细节,大概意思就是这样。

    4个角色都是在同样一个作用域下进行的,如果这个线程是主线程,那么Thread就是 ActivityThread(主线程,没错,主线程的类,就是它)

    解释一下:

    ThreadLocal负责存储当前线程的Looper对象。

     Looper负责对MessageQueue进行循环

    MessageQueue负责对msg进行暂时存储,并且在next出了 msg之后,调用dispatchMessage告诉handler执行消息

    Handler创建的时候,需要Looper作为参数,它提供sendMessage/post方法供外界调用;

    以上就是我们一个Thread作用域下的消息机制全图。

    结语

    学而时习之,不亦乐呼。温故而知新,可以为师矣。常总结,常进步。

    共勉! 

     

  • 相关阅读:
    Spring 定时器的使用
    spring MVC 资料
    Thrift入门及Java实例演示<转载备用>
    json数组转数组对象
    UiPath Outlook邮件正文引用图片
    UiPath 执行VBA代码Selection.Copy复制不生效
    RPA工程师学习路径是怎样的?企业面试开发者从哪些方面考察?
    一个RPA项目需要部署多少个机器人
    未来的企业软件和RPA
    RPA——被遮住的代码
  • 原文地址:https://www.cnblogs.com/hankzhouAndroid/p/9720150.html
Copyright © 2020-2023  润新知