Android:Handler的机制

Android 消息循环流程图如下所示:


image.png

总体来说:

 Handler 发送的消息由 MessageQueue 存储管理,并由 Looper 负责回 调消息到 handleMessage()。
 线 程 的 转 换 由 Looper 完 成 , handleMessage() 所 在 线 程 由 Looper.loop() 调用者所在线程决定。

主要涉及的角色如下所示:

 message:消息。
 MessageQueue:消息队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message。读取会自动删除消息,单链表维护,插入和删除 上有优势。在其 next()方法中会无限循环,不断判断是否有消息,有就返 回这条消息并移除。
 Looper:消息循环器,负责关联线程以及消息的分发,在该线程下从 MessageQueue 获取 Message,分发给 Handler,Looper 创建的时候会
创建一个 MessageQueue,调用 loop()方法的时候消息循环开始,其中 会不断调用 messageQueue 的 next()方法,当有消息就处理,否则阻塞在 messageQueue 的 next()方法中。当 Looper 的 quit()被调用的时候会调用 messageQueue 的 quit(),此时 next()会返回 null,然后 loop()方法也就跟 着退出。
 Handler:消息处理器,负责发送并处理消息,面向开发者,提供 API, 并隐藏背后实现的细节。
整个消息的循环流程还是比较清晰的,具体说来:
 1 、 Handler 通 过 sendMessage() 发 送 消 息 Message 到 消 息 队 列 MessageQueue。
 2、Looper 通过 loop()不断提取触发条件的 Message,并将 Message 交 给对应的 target handler 来处理。
 3、target handler 调用自身的 handleMessage()方法来处理 Message。 事实上,在整个消息循环的流程中,并不只有 Java 层参与,很多重要的工作都 是在 C++层来完成的。我们来看下这些类的调用关系。

注:

在这些类中 MessageQueue 是 Java 层与 C++层维系的桥梁,MessageQueue 与 Looper 相关功能都通过 MessageQueue 的 Native 方法来完成,而其他虚线连接 的类只有关联关系,并没有直接调用的关系,它们发生关联的桥梁是 MessageQueue。

常见面试题:

1.Handler 引起的内存泄露原因以及最佳解决方案

Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。 这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就 导致 Activity 泄露。
解决:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并在 Acitivity 的 onDestroy()中调用 handler.removeCallbacksAndMessages(null)及时 移除所有消息。
参考代码如下:

private static class SafeHandler extends Handler {

    private WeakReference ref;

    public SafeHandler(HandlerActivity activity) {
        this.ref = new WeakReference(activity);
    }

    @Override
    public void handleMessage(final Message msg) {
        HandlerActivity activity = ref.get();
        if (activity != null) {
            activity.handleMessage(msg);
        }
    }
}

并且再在 Activity.onDestroy() 前移除消息,加一层保障:

@Override
protected void onDestroy() {
  safeHandler.removeCallbacksAndMessages(null);
  super.onDestroy();
}

2.为什么我们能在主线程直接使用 Handler,而不需要创建 Looper ?子线程使用Hander的步骤?

通常我们认为 ActivityThread 就是主线程。事实上它并不是一个线程,而是主 线程操作的管理者。在 ActivityThread.main() 方法中调用了 Looper.prepareMainLooper() 方法创建了 主线程的 Looper ,并且调用了 loop() 方法,所以我们就可以直接使用 Handler 了。 因此我们可以利用 Callback 这个拦截机制来拦截 Handler 的消息。如大部分 插件化框架中 Hook ActivityThread.mH 的处理。
在 ActivityThread.main() 方法中有如下代码:

//android.app.ActivityThread
public static void main(String[] args) {
  //...
  Looper.prepareMainLooper();

  ActivityThread thread = new ActivityThread();
  thread.attach(false);

  if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
  }
  //...
  Looper.loop();

  throw new RuntimeException("Main thread loop unexpectedly exited");
}

Looper.prepareMainLooper(); 代码如下:

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

可以看到在 ActivityThread 里 调用了 Looper.prepareMainLooper() 方法创建了 主线程的 Looper ,并且调用了 loop() 方法,所以我们就可以直接使用 Handler 了。
所以子线程使用handler,创建Looper必须使用prepare()---->loop()--->quit();

3.主线程的 Looper 不允许退出

主线程不允许退出,退出就意味 APP 要挂。
调用Looper.quit()最终会调用MessageQueue的quit()方法:
MessageQueue中代码如下:

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
    、、、、、、、、、、、、、、、、
    }

4.Handler 里藏着的 Callback 能干什么?

来看看 Handler.dispatchMessage(msg) 方法:

public void dispatchMessage(Message msg) {
  //这里的 callback 是 Runnable
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    //如果 callback 处理了该 msg 并且返回 true, 就不会再回调 handleMessage
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    handleMessage(msg);
  }
}

可以看到 Handler.Callback 有优先处理消息的权利 ,当一条消息被 Callback 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截,那么就意味着一个消息可以同时被 Callback 以及 Handler 处理

这个就很有意思了,这有什么作用呢?

我们可以利用 Callback 这个拦截机制来拦截 Handler 的消息!

场景:Hook [ActivityThread.mH], 在 ActivityThread 中有个成员变量 mH ,它是个 Handler,又是个极其重要的类,几乎所有的插件化框架都使用了这个方法。

5.创建 Message 实例的最佳方式

为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候 尽量复用 Message ,减少内存消耗:
 通过 Message 的静态方法 Message.obtain();
 通过 Handler 的公有方法 handler.obtainMessage()。


总结发言:

  • Handler 的背后有 Looper、MessageQueue 支撑,Looper 负责消息分发,
  • MessageQueue 负责消息管理;
  • 在创建 Handler 之前一定需要先创建 Looper;
  • Looper 有退出的功能,但是主线程的 Looper 不允许退出;
  • 异步线程的 Looper 需要自己调用 Looper.myLooper().quit(); 退出;
  • Runnable 被封装进了 Message,可以说是一个特殊的 Message;
  • Handler.handleMessage() 所在的线程是 Looper.loop() 方法被调用的线程,也可以说成
    Looper 所在的线程,并不是创建 Handler 的线程;
  • 使用内部类的方式使用 Handler 可能会导致内存泄露,即便在 Activity.onDestroy 里移除延时消息,必须要写成静态内部类;

参考:https://juejin.im/post/6844903783139393550

你可能感兴趣的:(Android:Handler的机制)