美文网首页
Android:Handler的机制

Android:Handler的机制

作者: ZSGZ_AD | 来源:发表于2020-11-18 18:25 被阅读0次

    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<HandlerActivity> 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的机制

          本文链接:https://www.haomeiwen.com/subject/vuipbktx.html