Android Handler消息机制

2019-09-28  本文已影响0人  彭空空

导读

由于我更新优化本篇文章中的笔误之处,导致文章莫名被删除,故此重新发布!

Android Handler消息机制

为了避免ANR,我们通常会把耗时操作放在子线程里面去执行,因为子线程不能更新UI,所以当子线程需要更新UI的时候就需要借助到Andriod的消息机制,也就是Handler机制了。那么其原理是什么呢?下面我们根据平时的使用一步一步来解读源码。
以上内容可能会产生的问题:

一. Handler的常见用法。

  1. 创建handler对象并重写handleMessage方法,以便于处理自己需要的逻辑。
private Handler handler = new Handler(){
  @Override
  public void handleMessage(Message msg){
    ...
  }
}
  1. 使用handler 对象发送消息(对象、载体)。
private void sendMsg(){
  Mssages msg = handler.obtainMessage();
  msg.what=what;
  msg.age1=age1;
  handler.sendMessageDelayed(msg,1000);
}
...

二. 代码分析

  1. 以上代码可以看出,在主线程创建了一个handler对象,并重构了handleMessage方法,然后通过handler发送消息对象,中间经过一系列处理后,handleMessage方法接收到传递的数据,就可以处理具体的逻辑了(如更新UI)。
  2. 以上代码可以看出,发送消息、最后处理数据时都使用的是Message对象,这是在源码设计层面时约定的媒介(载体、中间件、快递),这里先不进行展开,只做一个简单的介绍:

what字段作为标识,
arg1/arg2字段作为简单类型的参数,
obj字段作为复杂对象,以及Bundle常见参数
target字段作为handler对象标识

  1. 再来看发送消息 sendMessageDelayed,通过源码可以发现所有的sendMessage方法执行后,最终都会走sendMessageAtTime()方法(这里就不贴上具体源码了)。
   public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

在这个sendMessageAtTime方法里会拿到一个MessageQueue的实例对象,并触发enqueueMessage(queue, msg, uptimeMillis)方法。那我们就先看看enqueueMessage方法,然后在去看mQueue怎么来的。

      boolean enqueueMessage(Message msg, long when) {
        ...
        Message p = mMessages;
        synchronized (this) {
         if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
            ...  
            Message p = mMessages;
            ...
            msg.next = p; // invariant: p == prev.next
            ...
        }
      }
        return true;
    }

首先这是一个被synchronized修饰的同步代码块,确保了并发问题;核心逻辑是对Message的实例对象p进行非空判断,最终两个两个逻辑里都会把msg放到Message实例对象p的next方法中进行排队。

4.现在回来研究这个mQueue对象是怎么来的呢,通过command+f(ctrl+f)搜索,最终发现是Handler构造方法中获取的(通过代码可以看出mQueue的获取是通过looper.mQueue,而我们前面在创建handler的时候使用的是无参构造的,那么这个Looper对象的实例额mLooper是怎么来的?

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

4.1 观察Handler的其他重载的构造方法发现,我们的无参构造最后会走如下构造,而这个mLooper实例对象是通过Looper.myLooper()方法获取的而且还不能为空!由此可以看出Looper、MessageQueue都是非常重要的对象。

   public Handler(@Nullable Callback callback, boolean async) {
       ...
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
    }

4.2 继续跟踪源码,进入Looper类发现myLooper()的内部是通过sThreadLocal.get()获取的,那么有get()自然是有set()的,所以我们command+f(ctrl+f)搜索找到了prepare()方法,而这个方法会被prepareMainLooper的方法执行,prepare翻译过来是准备的意思,prepareMainLooper就是准备主Looper的意思了,这里要注意有synchronized修饰噢。

   ...
   public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
   ...
   private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
   ...
   public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

4.3 到这里,我们知道了Handler无参构造的时候其实是使用了一个叫MainLooper及MessageQueue的对象,那这个MainLooper具体又是在哪里创建的呢,那就查查这个prepareMainLooper()方法在哪里被调用的使用command+shift+f进行全局搜索:


WX20200201-115959@2x.png

搜索出来4个结果,除了Looper.java外,SystemServer.java是系统级别的这里不做深入,后续新增该类的解读; Bridge.java是桥梁类,注释中写着“Main entry point of the LayoutLib Bridge.”,这里也不做深入;最后是ActivityThread.java类,也是这里需要重点关注的类:

public final class ActivityThread extends ClientTransactionHandler {
  ...
   public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        ...
        Looper.loop();
        ...
    }
  ...
}

有两个重大发现:
1、Looper.prepareMainLooper()是在一个叫ActivityThread 的 final 类型的Java类,的main(String[] args) 方法中执行的。
2、还执行了Looper.loop();
main是Java的入口方法,说明什么呢,在整个(应用)代码里Looper.prepareMainLooper()是最先执行的代码之一,也就是说Handler所使用的Looper早就初始化好了,而且是同步唯一的(如果忘了请回翻 )。

接下来看看loop()方法

 public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        ...
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ...
            msg.target.dispatchMessage(msg);
            ...
            msg.recycleUnchecked();
        }
    }

这里可以看出几个重要消息
1、Looper、MessageQueue是不可缺少的对象
2、使用到了for (;;) ,就是说无限循环,一直去MessageQueue的next()去查有没有Message对象,
3、有Message对象时会执行msg.target.dispatchMessage(msg);方法
当然也会有2个困惑的问题
3.1、for (;;) 这个无限循环不会让APP卡死吗?
3.2、 有一行代码if (msg == null) return 其注释是的意思是是阻塞,return之后不是继续for循环吗?

4.4 继续跟踪 msg.target.dispatchMessage(msg)方法,前面有提到target其实就是Handler(为什么呢,大家有兴趣可以看看源码)也就是说dispatchMessage方法是Handler类下的

   public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

发现这里调用了一个很眼熟的方法名handleMessage 没错,这就是一开始我们重写的的handleMessage方法,至此,脑海中是否已经有了一个Handle整体轮廓图?是否还有一个疑问,Handler啥时候把线程给切换了?

总结

1.APP在启动的时候,最先启动ActivityThread类的main方法,其中Looper的初始化工作和准备工作就是这时候执行的(获得Looper对象、MessageQueue)
2.Looper开启loop()方法(永动机)去死循环遍历MessageQueue的消息队列(至于为什么没有卡死前面有提到?有提到吗,为什么我修改的时候发现没有,捂脸)
3.在通常用法中创建Handler对象时,构造方法会拿到looper、mQueue对象
4.使用Handler对象发送消息(sendXxx())
5.把消息加入队列(mQueue.enqueueMessage)
6.第2步的永动机读取MessageQueue的消息
7.执行dispatchMessage方法
8.执行代码中创建Handler方法时重写的handleMessage方法完成Handler机制的整个过程。

最后,让我们带着文章中的问题,进行继续深入:Android中为什么主线程不会因为Looper.loop里的无限循环ANR?

上一篇下一篇

猜你喜欢

热点阅读