Handler消息机制

Android 的 Handler 机制主要用于在不同线程之间进行通信和消息传递,使得可以在子线程中执行耗时操作,然后通过 Handler 将相关消息发送到主线程进行 UI 更新等操作。

Handler 机制涉及到几个重要的类和对象,包括 Handler、Looper、MessageQueue 和 Message,其工作原理可以通过以下类比来理解:

  • Handler:类比为快递员,负责接收、发送和处理消息。

  • Message:类比为包裹,可以携带各种信息。

  • MessageQueue:类比为快递分拣中心的传送带,用于存储和管理消息。

  • Looper:类比为快递公司,管理 MessageQueue 并不断从队列中取出消息进行处理。

下面对每个部分进行详细介绍:

Message(消息)

Message 类位于 android.os 包中,它有一个无参构造方法,同时也提供了一些静态方法来创建实例。例如 obtain() 方法会从全局池中获取可用的 Message 对象,如果没有则创建新的。这样可以避免频繁创建对象,节省内存资源。Message 类中有几个重要成员变量:

  • target:Handler 对象,用于指定消息要发送给哪个 Handler 进行处理。

  • callback:Runnable 对象,与 Handler 配合使用。

  • what、arg1、arg2、obj:可用于存储消息的相关信息。

  • next:Message 类型,用于在 MessageQueue 中形成链表结构。

Handler(处理机)

Handler 类也在 android.os 包中,它的构造方法有多个。其中一个重要的构造方法如下:

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

在构造方法中,Handler 会与一个 Looper 和对应的 MessageQueue 关联起来。通过 Handler 可以发送消息到关联的 MessageQueue 中。

Handler 主要用于发送和处理消息。发送消息的方法有多种,如 sendMessagepost 等,无论使用哪种方式,最终都会调用 enqueueMessage 方法将消息加入到 MessageQueue 中。

Looper(消息循环器)

Looper 扮演着管理 MessageQueue 的角色。每个线程最多只能拥有一个 Looper。在主线程创建时,系统会自动创建一个默认的 Looper 对象,同时也会创建一个 MessageQueue。而其他非主线程默认是没有 Looper 和 MessageQueue 的,如果需要在非主线程中使用 Handler,就需要手动调用 Looper.prepare() 方法来创建 Looper 和关联的 MessageQueue。

通过调用 Looper.loop() 方法,会开启一个死循环,不断从 MessageQueue 中取出消息,并交由对应的 Handler 进行处理。如果 MessageQueue 中没有消息,queue.next() 方法会阻塞线程,直到有新的消息加入。

工作流程

  1. 在子线程中进行相关操作后,创建 Message 对象来封装要传递的信息。

  2. 通过 Handler 将 Message 发送到与该 Handler 关联的 MessageQueue 中排队。

  3. 调用 Looper.loop() 方法启动消息循环,Looper 不断从 MessageQueue 中取出消息。

  4. 根据 Message 中的 target 找到对应的 Handler,并调用其 dispatchMessage 方法进行消息分发处理。

  5. Handler 接收到消息后,根据具体情况在相应的方法(如 handleMessage 或设置的 Callback 中的方法)中进行处理,例如更新 UI 等操作。

这样就实现了在不同线程之间进行消息传递和处理,使得可以在子线程中进行耗时操作,避免阻塞主线程,同时又能安全地在主线程中更新 UI 或执行其他需要在主线程进行的操作。

需注意的是,在使用 Handler 时,如果是在非静态内部类的方式创建 Handler,并且该内部类持有外部类(如 Activity)的引用,可能会导致内存泄漏。因为当外部类(如 Activity)已经不再使用,但由于 Handler 还持有其引用,导致垃圾回收器无法回收外部类对象。解决方法可以是将 Handler 定义为静态内部类,并通过弱引用来引用外部类对象,以避免内存泄漏问题。

内存泄露问题

在 Android 中,Handler 可能会导致内存泄漏,主要是因为如果一个 Handler 被一个 Activity 或其他有生命周期的对象持有,而这个 Handler 又在异步任务中执行操作,可能会导致持有 Handler 的对象无法被垃圾回收,从而造成内存泄漏。

以下是关于 Handler 如何防止内存泄漏以及相关的垃圾回收(GC)引用链分析:

一、Handler 导致内存泄漏的原因及引用链

  1. 引用关系形成

    • 当一个 Activity 创建一个 Handler 实例时,Handler 内部默认会持有 Activity 的引用(通常是通过隐式的方式,例如通过 LooperMessageQueue间接持有创建它的上下文环境的引用)。

    • 如果这个 Handler 被用于在异步任务(如发送延迟消息)中执行操作,即使 Activity 已经完成了它的生命周期(比如已经被销毁),但由于 Handler 仍然持有 Activity 的引用,垃圾回收器无法回收这个 Activity 对象。

  2. GC 引用链示例

    • 假设存在一个 Activity A,在 A 中创建了一个 Handler H。

    • Handler H 通过 LooperMessageQueue与 Activity A 建立了引用关系。

    • 当 Activity A 应该被回收时,但由于 H 持有 A 的引用,所以 A 不能被回收。垃圾回收器在判断对象是否可回收时,会沿着引用链进行检查。如果从根对象(如静态变量、正在运行的线程等)可以到达一个对象,那么这个对象就不会被回收。在这种情况下,从根对象可以通过 Handler H 到达 Activity A,所以 A 不会被回收,从而导致内存泄漏。

二、防止 Handler 内存泄漏的方法

  1. 使用静态内部类+弱引用

    • 创建一个静态内部类 MyHandler,在这个内部类中持有一个对外部类(如 Activity)的弱引用。

    • 这样,Handler 就不会阻止外部类被垃圾回收,因为弱引用不会阻止垃圾回收器回收其所引用的对象。

    • 代码示例:

      public class MyActivity extends AppCompatActivity {
        private static class MyHandler extends Handler {
          private final WeakReference
                
        mActivityReference;     MyHandler(MyActivity activity) {       mActivityReference =  new WeakReference<>(activity);     }      @Override      public void handleMessage(Message msg) {       MyActivity activity = mActivityReference.get();        if (activity!=  null) {          // 在这里处理消息       }     }   }    private MyHandler mHandler;    @Override    protected void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);     mHandler =  new MyHandler( this);      // 发送消息等操作   }    @Override    protected void onDestroy() {      super.onDestroy();      // 在 Activity 销毁时,移除所有消息,防止在 Activity 已销毁的情况下处理消息     mHandler.removeCallbacksAndMessages( null);   } }
  2. 在合适的时机移除消息

    • 在 Activity 的 onDestroy方法中,确保调用 HandlerremoveCallbacksAndMessages(null)方法,移除所有未处理的消息和回调。这样可以避免在 Activity 已经销毁后,Handler 仍然尝试处理消息并访问已经不存在的 Activity 对象。

通过以上方法,可以有效地防止 Handler 导致的内存泄漏问题。在开发中,需要注意正确处理 Handler 的生命周期,以确保应用的性能和稳定性。

--- END ---