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组件,只能在UI线程访间。因此,Android系统将大部分耗时、繁重任务交给子线程完成,不会在主线程中完成,解决了第一个难题;同时,Android 只允许主线程更新 UI 界面,子线程处理后的结果无法和主线程交互,即无法直接访问主线程,这就要用到Handler机制来解决此问题。

Handler 机制核心类介绍

在 Handler 机制中的所有故事都是围绕Handler、Looper、Message 这3个类展开的。下面我们分别介绍一下这3个类。

1、Message

消息对象,顾名思义就是记录消息信息的类。Message 类有儿个比较重要的字段,如下表1-1所示。

                                                    表1-1Message类的常用字段

字段名

作用

arg1

使用这个字段来传递整数类型的值,与ag2相同

arg2

使用这个字段来传递整数类型的值,与ag1相同

obj

 这个字段是Object 类型,可以通过这个字段传递某个复杂的消息内容到接收者中

what

这个字段是消息的标志,在消息处理中,可以根据这个字段区分消息,类似于在处理 button事件时通过 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、Looper

Looper是 MessageQueue 的管理者。

MessageQueue 是一个消息队列,用来存放 Message 对象的数据结构,按照"先进先出"的原则存放消息。存放并非实际意义的保存,而是将 Message 对象以链表的方式串联起来的。MessageQueue 对象不需要自己创建,而是由Looper 对象对其进行管理,一个线程最多只可以拥有一个MessageQueue。可以通过Looper.myQueue()获取当前线程中的 MessageQueue。

在一个线程中,如果存在 Looper 对象,则必定存在 MessageQueue 对象,并且只存在一个Looper 对象和一个MessageQueue 对象。在 Android系统中,除了主线程有默认的Looper 对象,其他线程默认是没有Looper 对象的。如果想让新创建的线程拥有 Looper 对象,首先应调用Looper.prepare()方法,然后调用Looper.loop()方法。

另外,如果想要获取一个已经存在的Looper对象,可以通过Looper.myLooper()获取,此外还可以通过Looper.getMainLooper()获取当前应用系统中主线程的Looper 对象。在这个地方有一点需要注意,假如 Looper 对象位于应用程序主线程中,那么 Looper.myLooper()和Looper.getMainLooper()获取的是同一个对象。

3、Handler

消息的处理者。一般情况下,会在子线程中通过 Handler 对象把 Message 对象发送到MessageQueue 中,然后在主线程中用该对象的 handleMessage(Message msg)方法接收Message 对象,再对UI进行操作。

Handler类的方法很多,常用的几种如下表所示。

方法

作用

public final boolean sendEmptyMessage(int what)

用于发送一个只包含 what 值的 Message

public final boolean sendMessage(Message msg)

用于发送一个 Message

public final boolean sendMessageDelayed(Message msg,long nms)

用于延迟发送一个 Message,延迟时长为nms(毫秒)

public final boolean hasMessages(int what)

用于判断消息队列中是否已经含有此what值的Message

public final boolean post(Runnable r)

用于提交一个任务,并立即执行

public final boolean sendMessageDelayed(Runabler, long nms)

用于提交一个延迟执行的任务,延迟时长

通过上面的学习,读者可能已经明白了Handler 机制是如何发送消息到 MessageQueue 中的,但是对于handleMessage(Message msg)方法为什么能够接收到 Message 还抱有疑问。其实这里面的重点在于Looper.loop()方法。此方法很重要,源码如下:

通过源码分析,可以知道这是通过一个死循环不断地调用 MessageQueue 的 next()方法,这个next()方法就是消息队列的出队方法。每当有一个消息出队,就将它传递到msg.target的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 thread)方法的逻辑很简单:如果当前的线程不等于UI线程(主线程),就去调用 Handler 的 post()方法,否则直接调用Runnable对象的run()方法。

源码下载地址:链接:https://pan.baidu.com/s/1ExoSxaQRdjmwUUnDuSbUIQ 提取码:43ka

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值