Handler 系列二:Handler

553 阅读5分钟

Handler

构造函数

  • 有两种方式:① 传入Looper参数 ② 无Looper参数。

带 Looper 参数

1633856714(1).png

无 Looper 参数

1633856857(1).png

  • 此时会调用 Looper.myLooper() 方法,获取当前线程的 Looper 对象。如果当前线程的 Looper 对象为空则会抛异常。
  • 也就是说,在创建 Handler 之前必须先调用 Looper.prepare() 方法来构造消息循环,或者在已经有 Looper 对象的线程中(比如主线程)创建 Handler。

消息发送

  • 发送消息的方式:sendMessage()/post(Runnable) 等一系列方法
  • 无论调用哪个方法来发送消息,最终会回调到以下两个方法来处理:① sendMessageAtTime() ② sendMessageAtFrontOfQueue()。而这两个方法内部,都调用了 Handler.enqueueMessage() 私有方法进行处理。
  • Handler.enqueueMessage():① 首先会对 Message 消息对象进行处理,将当前 Handler 对象赋值给 Message.target 成员变量进行保存,② 最后调用 MessageQueue.enqueueMessgae() 将处理后的消息对象插入到消息队列中。

1633707075(1).png

消息处理

  • Handler.dispatchMessage(Message):该方法是运行在创建 Handler 时所使用的 Looper 对象中执行的,这样就将代码逻辑切换到指定的线程中去执行了。

1633703838(1).png

  • 判断 Message 消息对象的 callback:Runnable 成员变量是否为空?
    • 否:调用 handleCallback(Message) 来处理消息,调用该 Runnable.run() 方法。
    • 是:下一步
  • 判断当前 Handler 对象的 mCallback:Handler.Callback 成员变量是否为空?
    • 否:调用 mCallback.handleMessage(Message) 方法来处理消息
      • 如果返回true,则消息处理结束。
      • 如果返回false,调用 handleMessage(Message) 来处理消息。
    • 是:调用 handleMessage(Message) 来处理消息;Handler的子类必须实现它才能接收消息。
  • 注意1:Message 消息对象的 callback:Runnable 成员变量
    • 如果是通过 Message.obtain(Handler h, Runnable callback) 构造消息对象时,则该成员变量不为空。
    • 也就是说,如果消息是通过 post(Runnable) 的方式发送的,则该对象不为空。
  • 注意2:Handler 对象的 mCallback:Callback 成员变量
    • 如果是通过传递了一个 Callback 参数的方式来构造 Handler 对象,则该成员变量不为空。

小结-Handler

  • 在创建 Handler 的时候,需要去指定其所关联的 Looper。如果没有指定 Looper 参数,则会通过 Looper.myLooper() 方法获取当前线程的 Looper 对象作为方法参数去构造 Handler。
    • 如果当前线程的 Looper 对象为空,则会抛异常。
    • 也就是说,在创建 Handler 之前必须先通过 Looper.prepare()、Looper.loop() 方法来构造并开启线程的消息循环,或者在已经有 Looper 对象的线程中去创建 Handler(比如主线程)。
  • Handler 持有两个关键的成员变量,一个是 Looper(从而实现Handler与指定线程相关联),另一个是保留了与其所绑定的 Looper 对象所关联的消息队列 MessageQueue 。
  • 主要用途:发送消息和处理消息。
    • 消息发送过程,就是将消息对象 Message 添加到与之绑定的 Looper 对象所关联的消息队列 MessageQueue 中。
    • 消息处理过程,就是在与之绑定的 Looper 对象所关联的线程 Thread 上去执行消息。
  • 主要作用:Handler 是 Android 里面实现线程间通信的工具,其主要作用就是将一个任务切换到特定线程中去执行。
    • 主线程(UI线程),用于处理与 UI 组件相关的、与用户操作相关的事件。
    • 子线程(工作线程),用于执行耗时操作,比如网络请求、数据加载、I/O 操作。
  • 主线程(UI线程)与子线程(工作线程)的通信媒介,是 Handler
  • Handler 与 MessageQueue 的通信媒介,是 Looper

为什么在子线程中创建 Handler 会抛异常?为什么主线程不会?

  • 线程默认是没有 Looper 的,因此在没有做任何处理的情况下在子线程中创建 Handler 时,会直接抛异常。
  • 而在主线程中构造 Handler 对象不会抛异常的原因是,主线程在启动时(ActivityThread.main() 方法)就已经创建并启动了消息循环,因此主线程的 Looper 对象不会为空,所以在主线程中创建 Handler 对象,即使没有指定 Looper 参数也不会抛异常。

Handler 的消息发送机制?

  • 无论是调用 post(Runnable)/sendMessage() 系列的方法,最后都会回调到 Handler.enqueueMessage() 方法进行处理。该方法中,会将当前 Handler 对象赋值给 Message.target 成员变量进行保存,最后调用 MessageQueue.enqueueMessgae() 将处理后的消息对象 Message 插入到消息队列 MessageQueue 中。
  • 由此可知,Handler 发送消息的过程,实际上就是往消息队列中插入一个消息。并且可以知道,消息对象 Message ,持有发送该消息的 Handler 对象的引用,最终也会将消息交给它来处理。

Handler 的消息处理机制?

  • Handler.dispatchMessage() 消息处理过程,分三步走。
  • ① 当 Message 消息对象的 callback:Runnable 成员变量不为空(即消息是通过 post(Runnable) 的方式发送时,该成员变量不为空),那么会直接调用该 Runnable.run() 方法来处理消息;否则进行下一步。
  • ② 当 Handler 对象的 mCallback:Handler.Callback 成员变量不为空(即构造 Handler 的时候传递了回调对象 Callback),则先调用 mCallback.handleMessage(Message) 方法来处理消息,如果该方法返回 true,则消息处理结束;如果该方法返回 false,则继续调用 Handler.handleMessage(Message) 来处理消息。
  • ③ 当 Handler 对象的 mCallback:Handler.Callback 成员变量为空,则会直接调用 Handler.handleMessage(Message) 来处理消息。
  • 由此可知,当通过 sendMessage() 的方式向 Handler 发送消息时,必须实现 Handler.handleMessage() 方法来接收和处理消息。

Handler 如何实现线程切换?

  • 创建 Handler 对象时,通过指定与其所绑定的 Looper,从而实现与指定的线程相关联,进而与该线程的消息队列 MessageQueue 相关联。
  • 消息发送过程:就是将消息对象 Message 添加到"与 Handler 绑定的 Looper 对象所关联的消息队列 MessageQueue 中"。
    • 一个线程有且只有一个 Looper,一个 Looper 有且只有一个 MessageQueue。
    • 从而将消息插入到指定线程所关联的消息队列中。
  • 消息处理过程:就是在与之绑定的 Looper 对象所关联的线程 Thread 上去执行消息。
    • Looper.loop() 方法在循环中从 MessageQueue.next() 方法得到需要被处理的消息对象 Message 之后,就会将该消息对象交给其成员变量 Message.target.dispatchMessage(Message) 进行处理,从而达到线程切换的目的。
    • Looper.loop() 方法的最后会将该消息对象 Message 进行回收,回收到消息池中以便复用。然后进入下一个循环。