前言

最近补了一波Handler的知识,之前写项目的时候也有用到Handler,就是一直没有完全理解其中的机制,现在大概来说说吧。


什么是Handler

首先在Android当中是存在UI线程的,而UI线程其实就是主线程。在java中,如果处理一些比较耗时的操作,譬如网络请求等待响应这一类操作,线程是会出现阻塞来等待请求返回的。而假如我们在UI线程当中来等待请求返回,也就是说UI线程出现阻塞。对于一个前端页面来说有可能就出现了卡顿现象。做前端的都知道,卡顿是很难受的。所以一般这种后台处理数据的逻辑会采取不影响UI层面的情况下执行,所以就采有了异步。Handler跟多线程,消息队列联系很紧密,其主要的功能就是发送和处理Message,也可以分发Runnable对象。 每个Handler实例,都会绑定到创建他的线程中(默认是位于主线程,但是我们可以通过Looper对象来指定到其他线程)。


以下是我从网上看到的Handler作用

  • 执行计划任务,比如在预定的实现某些任务,模拟定时器等等。
  • 线程之间的互相通信,在app启动的时候,会创建一个主线程(即UI线程),主线程创建了之后还会创建一个消息队列来处理各种消息,当我们创建了子线程时,可以在子线程中拿到主线程创建的Handler对象,当我们需要通信时就可以,通过该对象像主线程的消息队列发送消息,主线程接收到消息之后就会处理。
  • 确保操作始终在某个特定的线程中运行。比如说当我们访问数据库并加载数据时,除了初始化要加载外,每当我们收到数据改变的消息通知时也需要重新加载,为了保证数据的有效性,减少不必要的查询操作。

消息循环机制

其实在主线程中是存在一个消息循环机制的。和Handler相关的其实还有两个角色。一个叫Looper、一个叫MessageQueue。这两个是什么东西?下面慢慢讲。

  • 首先先讲一个网购的故事。小羊从淘宝上买一个手机壳,然后在确认付款之后就会到达待收货阶段。这时候,店家就会根据小羊提供的地址将快递单填好然后交给快递员小哥。快递员小哥就会按照填写的地址,把这个快递送到小羊的手上。这就是整个淘宝购物最简单的流程了。

好了,说完一个小羊购物的故事后就到了和Handler关联的时间了。这里先分一下角色,上述的Handler就是小羊,小羊购买的货物是一条message店家在这里办理一个Looper的角色,而快递员小哥就是MessageQueue了。那么究竟这些是什么跟什么呢?下面讲解一下:

  • Looper:Android的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper。我们的主线程(UI线程)就是一个消息循环的线程。Looper的线程,不管是UI线程还是子线程,只要你有Looper,我就可以往你的消息队列里面添加东西,并做相应的处理。一句话每一个消息循环线程都可以产生一个Looper对象,用于管理线程里的MessageQueue。
  • Handler:你可以**构造Handler对象来和Looper沟通以便Push新的消息到MessageQueue
    里或者接收Looper(从MessageQueue中取出)发送过来的消息**
  • MessageQueue: 用于存放线程放入的消息

注:主线程默认含有Looper、MessageQueue
现在翻译一下上述的故事就是:

  1. 首先,Handler(小羊)会向Looper(店家)提交一条message(里面包含要买的货物)。
  2. Looper(店家)收到请求后就向MessageQueue(快递员)增加这条信息。(相当于生成了一条订单)。
  3. 当MessageQueue里轮到该条message时,就会响应Handler的handlerMessage,让其处理这条消息。(相当于快递员将货物送到小羊手上,小羊要自己处理这个货物)

构建Handler对象

  • 默认的构造方法Handler()把当前Handler与当前线程和当前线程的消息队列绑定,这也是为什么默认的Handler一定运行在主线程中
  • 可以触发回调Handler(Handler.Callback callback)
  • Handler(Looper looper) 把Handler绑定到Looper所属的线程和消息队列中
  • Handler(Looper looper, Handler.Callback callback)

Handler的常用方法

以下方法名摘录自网上

  • 获取Looper对象: Looper getLooper()
  • 处理系统的消息:void dispatchMessage(Message msg) 一般不宜修改
  • 处理消息:void handleMessage(Message msg) 必须实现
  • 从消息队列中得到新的Message对象:Message obtainMessage()及重载
  • 发送Runnable对象:boolean post(Runnable r) 及重载形式
  • 发送消息:boolean sendEmptyMessage()及重载
  • 发送自定义Message:boolean sendMessage(Message msg)
  • removeCallbacks(Runnable r) 、removeCallbacksAndMessages(Object token)
    、removeMessages(int what) 等移除系列,移除了之后就不会去执行handleMessage(Message msg)

注:如果想将已发出的消息移除,可以使用removeCallbacks(Runnable r) 、removeCallbacksAndMessages(Object token)
、removeMessages(int what),具体视情况而定。但是需要注意的是,removeCallbacks(Runnable r)的该Runnable对象如果已经提交给handler进行处理当中的话,是无法实现移除的。


Handler在其他线程的使用

值得一提的是,我们无法在其他线程中直接创建Handler,因为其他线程默认不自动创建Looper和MessageQueue。
我们可以通过创建一个继承HandlerThread类的子线程,并且在HandlerThread.onLooperPrepared()方法中来进行Handler的相关操作。HandlerThread是自带Looper、MessageQueue的。


使用Looper对象创建Handler

Looper.getMainLooper()方法可以获取到主线程的Looper对象。
在其他线程中也可以通过this.getLooper();获取。
创建Handler对象handler=new Handler(Looper.getMainLooper())


handleMessage

这里说一下handleMessage这个方法:
当消息需要处理时就会响应handleMessage

handler=new Handler(){

@Override
public void handleMessage(Message msg) {
    super.handleMessage(msg);
    
   }

};

方法的模板就是上述的结构了。然后上述的Message对象就会携带一个what的整型的基本数据类型。通过这个what的值就可分辨出是你所传递的哪条消息。


handleMessage的处理唯一性

这个处理唯一性其实我自己起的名字。意思就是在这个方法还没完全执行完的时候,这个message对象是不会更改的。譬如

handler=new Handler(){

@Override
public void handleMessage(Message msg) {
    super.handleMessage(msg);
    Log.i("handler",msg.what);
    Thread.sleap(1000);
    Log.i("handler",msg.what);
   }
};

以上首先在接收到message的时候先在log中输出一个what,然后再让他睡眠1s,也就是线程阻塞。然后再打印这个what。如果是运行后你会发现这还是同一条消息。


跨线程通信

跨线程通信的意思就是我们可以使用handler来进行线程间的交互。
首先有主线程和线程A,线程A是子线程

  • 首先我们在线程A中创建一个主线程的Handler,handler=new Handler(Looper.getMainLooper())
  • 然后我们就可以通过这个handler对象来向主线程发送消息,然后在主线程做处理。

最后

最后还要提一个点就是handler对象和MessageQueue的地址是有唯一性的。也就是说其中其实是有target这个概念的,不可以妄想使用两个不同的handler对象来共同处理一个message。以下是错误想法

  • 新建一条message,然后使用handlerA将message送进入消息队列。
  • 使用handlerB进行这条message的处理。

以上这种做法是完全不可行的,即使这条message是相同的,但是不同的handler只能处理自己发送的消息。

标签: Handler详解

添加新评论