tag 标签: 消息处理机制

相关博文
  • 热度 16
    2021-10-31 18:39
    1426 次阅读|
    0 个评论
    Android 消息处理机制
    Android 应用程序启动时,系统会创建一个主线程,负责与 UI 组件( widget 、 view )进行 交互 ,比如控制 UI 界面显示、更新等 ; 分发事件给 UI 界面处理,比如按健事件、触摸事件、基绘图事件等,因此, Android 主线程也称为 UI 线程。 由此可知, UI 线程只能处理一些简单的、短暂的操作,如果要执行繁重的任务或者耗时长的操作,比如访问网络、数据库、下载等,这种单线程模型会导致线程运行性能大大降低其至阻塞 UI 线程,如果被阻塞超过 5 秒,系统会提示应用程序无响应,也就是 ANR , 这会 直接 导致退出整个应用程序或者短暂杀死应用程序。 除此之外,单线程模型的 UI 主线程也是不安全的,会造成不可确定的结果。线程不安全可以简单理解为 ∶ 多线程访问资源时,有可能出现多个线程先后更改数据造成数据不一致。比如 , A 工作线程(也称为子线程)访问某个公共 UI 资源, B 工作线程在某个时候也访问了该公共资源,当 B 线程正访问时,公共资源的属性已经被 A 改变了,这样 B 得到的结果不是所需要的,造成了数据不一致的混乱情况。 线程安全简单理解为 ∶ 当一个线程访问功能资源时对该资源进行了保护,比如加了锁机 制, 当前线程在没有访问结束释放锁之前,其他线程只能等待直到释放锁才能访问。这样的线程就是安全的。 基于以上原因, Android 的单线程模型必须遵守两个规则 ∶ ( 1 )不要阻塞 UI 线程。 ( 2 )不要在 UI 线程之外访问 UI 组件,即不能在子线程访问 UI 组件,只能在 U I 线程访间。因此, Android 系统将大部分耗时、繁重任务交给子线程完成,不会在主线程中完成,解决了第一个难题 ; 同时, Android 只允许主线程更新 UI 界面,子线程处理后的结果无法和主线程交互,即无法直接访问主线程,这就要用到 Handler 机制来解决此问题。 Handler 机制核心类介绍 在 Handler 机制中的所有故事都是围绕 Handler 、 Looper 、 Message 这 3 个类展开的。下面我们分别介绍一下这 3 个类。 1 、 Message 消息对象,顾名思义就是记录消息信息的类。 Message 类有儿个比较重要的字段,如 下表 1-1 所示。 表 1-1 Message 类的常用字段 字段名 作用 arg1 使用这个字段来传递整数类型的值,与 ag2 相同 arg2 使用这个字段来传递整数类型的值,与 ag 1 相同 obj 这个字段是 Object 类型,可以通过这个字段传递某个复杂的 消息内容到接收者中 what 这个字段是消息的标志,在消息处理中,可以根据这个字段区分消息,类似于在处理 but t on 事 件 时通过 switch ( v . getId ()) 判断 是点击了哪个按钮 在使用 Message 时,可以通过 new Message () 创建一个 Message 实例,但是 Android 官更推荐我们通过 Messge.obtain () 或者 Hander.obtainMesage () 获取 Message 对象。这并不一定是直接创建一个新的实例,而是先从消息池中看有没有可用的 Message 实例,存在则直接取出并返回这个实例。反之如果消息池中没有可用的 Message 实例,则根据给定的参数新建一个新 Message 对象。一般情况下, Android 系统默认情况下在消息池中实例化 10 个 Message 对象 。 当 Message 实例被创建之后,使用 setDate () 或者 arg 参数为 Message 携带一些数据,并通过 Handler 对象发送到 MessageQueue 中。 2 、 L o oper Loope r 是 MessageQueue 的管理者。 Mes s ageQueue 是一个消息队列,用来存放 Me s sage 对象的数据结构,按照 " 先进先出 " 的原则存放消息。存放并非实际意义的保存,而是将 Message 对象以链表的方式串联起来的 。 Mes s ageQueue 对象不需要自己创建,而是由 Looper 对象对其进行管理,一个线程最多只可以拥有一个 MessageQueue 。可以通过 Looper.myQueue () 获取当前线程中的 Message Q ueue 。 在一个线程中,如果存在 Looper 对象,则必定存在 MessageQueue 对象,并且只存在 一 个 Looper 对象和一个 MessageQueue 对象。在 Android 系统中,除了主线程有默认的 Lo o p er 对象,其他线程默认是没有 Looper 对象的。如果想让新创建的线程拥有 Looper 对象,首先应调用 Looper.prepare () 方法,然后调用 Looper.loop () 方法。 另外,如果想要获取一个已经存在的 Looper 对象,可以通过 Looper.myLooper () 获取,此外还可以通过 Looper.getMai n Looper () 获取当前应用系统中主线程的 Looper 对象。在这个地方有一点需要注意,假如 Looper 对象位于应用程序主线程中,那么 Looper.myLooper () 和 Looper.getMainLo o per () 获取的是同一个对象。 3 、 Handler 消息的处理者。一般情况下,会在子线程中通过 Handler 对象把 Message 对象发 送到 MessageQueue 中,然后在主线程中用该对象的 handleMessage ( Me s sage msg ) 方法接收 M essage 对象,再对 UI 进行操作。 Handler 类的方法很多,常用的几种如 下 表所示。 方法 作用 public fi nal boo l ean sendEmptyMess a ge( in t what ) 用于发送一个只包含 what 值的 Mes s age public f i nal b oolean sendMessage(Message msg) 用于发送一个 Message public fi nal bo o lean sendMessageDelayed(Message msg,long nms ) 用于延迟发送一个 Message ,延迟时长为 nms (毫秒) public f i nal boole a n hasMessages(int what ) 用于判断消息队列中是否已经含有此 what 值的 Message public f i nal boole a n post(Runnable r) 用于提交一个任务,并立即执行 public f i nal boole a n sendMessageDelayed(Runabler, long nms ) 用于提交一个延迟执行的任务,延迟时长 通过上面的学习,读者可能已经明白了 Handler 机制是如何发送消息到 MessageQueue 中的,但是对于 handleMessage ( Message msg ) 方法为什么能够接收到 Mes s age 还抱有疑问。其实这里面的重点在于 Looper.loop ()方法。此方法很重要,源码如下 : 通过源码分析,可以知道这是通过一个死循环不断地调用 MessageQueue 的 nex t() 方法 , 这个 next () 方法就是消息队列的出队方法。每当有一个消息出队,就将它传递到 msg.targ e t 的 dispatchMessage ( Message msg )方法中。 dispatchMessage ( Message msg ) 的源码如下 ∶ 这样一来, handleMessage ( Message msg ) 方法才可以获取到之前发送的消息。 另外, 上一篇博客的实例 中用到了 runOnUiThread ( Thread thread ) 方法,当时说这其实使用的也 Handler 机制。打开 runOnUiThread ( Thread thread ) 方法,源码如下 ; public final void runOnUiThread(Runnable action) { if(Thread.currentThread() != mUiThread) { mHandler . post(action); } else{ action.run(); } } 通过分析发现, runOnUiThread ( Thread t h read ) 方法的逻辑很简单 : 如果当前的线程不等于 UI 线程(主线程),就去调用 Handler 的 post () 方法,否则直接调用 Runnable 对象的 run () 方 法。 android studio工具及手机模拟器以及更多工程源代码下载请前往微信公众号:大鸟科创空间,回复:android studio即可获取。