Android 11 源码分析 :致敬Handler(1)

581 阅读9分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

现在有了RxJava,Kotin,EnentBus等,开发者对于使用Handler的机会越来越来少。那我们是不是可以就不去了解Handler机制了?不行。为什么?因为面试会问!

言归正传,我刚入行的时候进公司看到的项目,都是Okhttp再加Handler的,虽然现在开发者很少直接使用Handler,但是据我所知,Rxjava,Kotlin的协程他们内部切换主线程内部都和Handler机制有关,我们用到的view.post,runOnUiThread内部都是Handler机制,还有我们分析Activity启动流程的时候发现Handler起了至关重要的作用。所以了解Handler机制很有必要。

概述

Handler有一下一句注释

1629793558(1).png

用人话说就是1.发消息2.切线程

我们知道说道Android消息机制是通过Handler,Looper,MessageQueue,Meassge这四个主要类完成的。

在Looper上面有官方对于Handler的使用样例

1629809330(1).png 可以看到Looper的官方注释也给了使用样例:

  1. Looper.prepare();
  2. 创建Handler
  3. Looper.loop();

当然还有2种使用方式:

  1. post(Runnable)
  2. senMessage()

Looper

在看Looper的prepare和loop这两个方法之前,先看一下他的构造方法。

# android.os.Looper

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class

final MessageQueue mQueue;

final Thread mThread;

private boolean mInLoop; 

private Looper(boolean quitAllowed) {
   mQueue = new MessageQueue(quitAllowed);
   mThread = Thread.currentThread();
}

Looper有几个比较关键的成员变量在代码中贴了上来,可以先留意一下。 构成方法接受一个参数,看意思是:是否允许退出,然后根据这个参数创建了一个MessageQueue给变量 mQueue,并且获取当前的线程,赋值给了mThread。

Looper内部有一个消息队列MessageQueue,还有一个当前运行在哪个线程的变量

  • MessageQueue是一个消息队列,内部存储着Measgae,根据quitAllowed的值,MessageQueue会有两种类型的,可退出和不可退出,后面会细讲。

Looper.prepare

# android.os.Looper

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
         hrow new RuntimeException("Only one Looper may be created per thread");
       }
       sThreadLocal.set(new Looper(quitAllowed));
}

Looper内部用ThreadLocal来存储Looper。也就是说,线程内可以拥有自己独有的Looper对象。也就是说,prepare内部通过ThreadLocal机制,巧妙的把线程和Looper对象进行了冠联。

不熟悉的ThreadLocal的小伙伴可以先自行百度一下,后期我也会写一篇关于ThreadLocal的文章

这边会先从sThreadLocal里做一个取的操作,如果有值则会抛异常Only one Looper may be created per thread

也就是说

线程的Looper.prepare只能调用一次,多次调用会报错。

Looper.loop

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    public static void loop() {
        final Looper me = myLooper();
        ......
        final MessageQueue queue = me.mQueue;
        ......
          for (;;) {
            Message msg = queue.next(); // might block 
            注意一下上面的注意might block  
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ......
            try {
                msg.target.dispatchMessage(msg);
                ......
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
               ......
            }
            ......
          }
   }

可以看到myLooper()方法是对sThreadLocal的一个取的操作。

先是获取当前线程的Looper对象,然后取出消息队列MessageQueue。然后通过开启一个死循环,循环内不断的从MessageQueue内取出消息,如果消息为null则退出循环。如果消息不为null,则调用消息内部的taget的dispatchMessage方法。 到这里出现几个问题:

  1. queue.next()什么时候为null?
  2. msg.target.dispatchMessage(msg)又做了什么?
  3. MessageQueue是什么
  4. Message是什么

这些问题后面会一一得到解答,暂时先看官方样例的第二步,创建Handler

Handler

Handler构造方法

# android.os.Handler

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

  1. 通过Looper.myLooper()给当前Handler的mLooper赋值
  2. 如果Looper.myLooper()返回null则会抛出我们很熟悉的异常:不能在没有执行Looper.prepare()的线程里创建Handler。
  3. 给取出mLooper下的mQueue赋值给当前Handler的mQueue

Looper.myLooper()为null的情况肯定是没有往sThreadLocal中添加值,也就是没有执行Looper.prepare()方法。

在创建Handler之前,该线程必须先执行Looper.prepare()方法.

post(Runnable)

public final boolean post(@NonNull Runnable r) {
   return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

post方法内部会把runnable类型的参数通过getPostMessage方法包装成Message类型,然后在调研, 我们知道post还可以延迟操作。也就是

public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

其实最后都是调用sendMessageDelayed方法

sendMessage

public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}

我们发现不管是post,postDelayed,sendMessage最后都是执行发送延迟消息的方sendMessageDelayed,只不过第二个参数是0。

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

sendMessageDelayed方法接受一个Message参数和一个表示时间的参数,为0则表示立刻执行,有值则在当前系统时间加上需要延迟的时候,传给sendMessageAtTime。

Android内能见到的延迟操作,绝大部分都是通过这种方式实现的,随意没啥神秘的。

    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);
    }
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        注意!注意!注意!    
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

sendMessageAtTime只是将当前线程下的Handler持有的消息列表传给了enqueueMessage方法。 enqueueMessage方法第一行就将当前Handler赋值给了Message的target变量。

也就是说每一个Message内部都会通过target持有发送这个消息的Handler引用。在回想起之前Looper从消息队列里取出Message之后会调用target.dispatchMessage(msg)方法。其实这就是触发消息的处理了。也是最重要的一行代码只是现在还不看不出来。对于Message持有Handler引用也好理解,就好像我们微信发出去的消息,每个消息都有对应这发送者是谁。

代码接下来应该来到queue.enqueueMessage,也就是去看看MessageQueue。但是看它之前,先简单看看Message是怎样的组成,毕竟MessageQueue存的就是这个东西。

Message

# android.os.Message

 public final class Message implements Parcelable {
   public int what;
   public Object obj;
   Runnable callback;
   public long when;
   Handler target;
}

Message可以看成我们平常写的一个数据接口,可以进行系列化。当然他还有其他的属性,我们先看看比较重要和熟悉的几个。

  • what : 我们在Handler中处理消息的区分依据
  • obj : 我们在消息中传递的数据,根据what区分后进行强转
  • callback : 需要处理的回调,上文看到我们通过post发送的callback就是被赋值在这
  • when : 消息的处理时间
  • target : 发生这个Message的Handler对象,最后通过他来完成消息的消费

MessageQueue

简单的了解了Message之后,接着看MessageQueue的enqueueMessage方法

# android.os.MessageQueue
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        ......
        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            对队列进行排序
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;     
    }

方法比较长,主要做了两件事:

  1. 校验
  • 如果Message的target为null,则抛异常,因为这是个幽灵消息,没有发送者也就没有执行者
  • msg.isInUse()表示消息正在使用,抛异常。可以创一个Message,连续两次调用send,则会触发
  • 如果MessageQueue正在退出,则抛异常
  1. 对队列进行排序。把传进来的诗句赋值给Message的when,也就是执行时间。然后对正规队列根据when,做一个排序。

MessageQueue是一个以时间为条件的,有序列表

简单梳理流程已知流程:

  1. 在线程中先执行Looper.prepare()方法,目的是为了让当前线程持有一个Looper对象。
  2. 创建Hander对象
  3. 执行Looper.loop方法。目的是为了开启消息的循环,取出消息后,执行msg.target.dispatchMessage(msg)方法,也就是触发了发送消息的Handler执行dispatchMessage方法。

那现在就是看Handler的dispatchMessage做了什么了。

其实从另一个角度,我们发现最后还是需要分析dispatchMessage。

在创建Handler的时候,我们需要重写handleMessage来坐接收到消息的处理。

1629893255(1).png

可以看到Handler的handleMessage是个空实现,上面的注释也是说。子类必须需要重写该方法完成对message的接收。 我们会发现,触发Handler的handleMessage方法的地方就是dispatchMessage。

dispatchMessage

1629894300(1).png 图中红框1处,如果message的callback不为null,则执行handleCallback。这种情况一般都是通过post的方式

private static void handleCallback(Message message) {
    message.callback.run();
}

其实就是执行 Runnable 的 run 方法,实际上就是执行一个接口回调,与线程无关。

图中红框3处,是当mCallback不为null的时候触发。mCallback不为null只有当我们构造Hanlder的時候传Cllback。

public Handler(@Nullable Callback callback) {
    this(callback, false);
}

这种使用情况非常少,可以选择忽略。

图中红框3处,是我们把Handler当成消息机制时,就会执行我们重写的handleMessage。

小结

  1. Handler 负责的是消息的发送和事件的处理
  2. 不管是 post(Runnable)还是sendMesage,最后触发的都是sendMessageDelayed,只不过Handler会将post的Runnable赋值给Message的callback,还是包装成了一个Message
  3. Message内部的target就是发送这个消息的Handler,所以才能在创建Hnadler的线程处理事件。这点特别重要

很早以前我有一种误解,安卓的消息机制是由 Message,MessageQueue,Looper,Handler互相调用一起组成的,以为Looper是从MessageQueue取消息交给Handler来处理的。后面自己看了源码才发现我错了。

  1. Message,MessageQueue只是两个数据结构。Looper的作用是不停的从MessageQueue里取出消息,但是并非是交给了Handler,而是触发了Message的target执行dispatchMessage。
  2. handler对于有Runnable消息处理相当于执行一个回调

最后再重复唠叨一句,Handler之所以能在别的线程发消息,却还能在创建Handler的线程执行的原因。就是因为发送的Message里携带了发送他的Handler引用,所以不管这个消息最后去了哪里。执行消息target.dispatchMessage的时候,执行的就是他所属Handler的dispatchMessage,然后再执行到Handler的handleMessage方法。我们又对handleMessage进行了重写,所以最后回到了创建Handler的线程里。

本文对Android消息机制做了个大概的分析,关于Handler还留下了很多面试会问到的疑问。将在下一篇对面试点进行点对点解析。