熟悉又陌生的Handler-1

260 阅读5分钟

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

熟悉又陌生的Handler-1

Handler三件套的创建流程:

构造方法如下:

public Handler(Callback callback, boolean async) {
    // 如果这个flag是true
    // 会在运行时检测Handler的实现是不是静态的(匿名内 || 内部类 || 局部类)
    // 会在运行时输出wraning提示可能造成内存泄漏
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    // 如果在调用Handler构造方法的时候,没有获取到当前所在线程的Looper对象
    // 直接抛出异常,因为这种状态下的Handler完全不可用
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    // 让当前Handler也持有当前线程的Looper的MessageQueue
    mQueue = mLooper.mQueue;
    mCallback = callback;
    // 标识这个Handler发送的Message是否都是异步消息(作用后面再分析)
    mAsynchronous = async;
}

而对于主线程而言,Looper的创建是在ActivityThread.main中完成的,通过调用prepareMainLooper:

public static void prepareMainLooper() {
    // 也是通过调用Looper.prepare进行创建,只不过quitAllowed设置为false
    // 表示这个Looper永远不会主动退出
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
           throw new IllegalStateException("The main Looper has already been prepared.");
        }
        // 将主线程的Looper对象引用赋值给sMainLooper这个静态变量
        sMainLooper = myLooper();
    }
}

Looper的创建都是通过调用prepare方法(如果我们需要自己创建一个Handler-Looper也一样是调用Looper.prepare这个静态方法):

// Looper对外暴露的prepare方法创建的Looper是会主动退出的
public static void prepare() {
    prepare(true);
}
// 这个静态方法只对内提供,
// 即quitAllowed=false只在创建主线程的Looper的时候允许
private static void prepare(boolean quitAllowed) {
    // sThreadLocal是一个线程唯一的容器,这里存放的是Looper对象
    // 如果当前线程已经创建过了Looper,再次调用prepare会报错
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 创建一个Looper对象并存入sThreadLocal
    sThreadLocal.set(new Looper(quitAllowed));
}

而在Looper的构造函数中,只做了两件事:

private Looper(boolean quitAllowed) {
    // 创建MessageQueue对象
    mQueue = new MessageQueue(quitAllowed);
    // 获取当前的线程实例
    mThread = Thread.currentThread();
}

而MessageQueue的构造,最主要的还是调用nativeInit完成Native侧的初始化工作,这个后面再做分析。

那我们先简单做个小结:

上面我们涉及到了几个对象:Handler,Looper,MessageQueue。这三个对象的作用,众所周知......

  • Handler:负责消息的发送和处理。
  • MessageQueue:存放Message对象的队列,本质上就是一个数据结构。
  • Looper:三件套的驱动,不断从消息队列中取消息然后发送给对应的Handler。

上面提到了一个Message对象,后面再分析,本质上也不过是一个存放一些数据的数据结构。

Looper.loop启动Looper的钥匙:

如果我们仅仅是构造了Handler和Looper对象,去发送消息,会发现整个系统并没有被运转起来,因为我们没有调用Looper.loop方法开启MessageQueue的循环。

在ActivityThread的main方法中,在创建了主线程的Looper之后,会调用Looper.loop开启消息循环,所以我们如果在主线程中创建的Handler,不需要调用什么其它Api,直接就可以发送消息了。

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.");
    }
    // 当前Looper的消息队列
    final MessageQueue queue = me.mQueue;
    // ...
    // 一个无限循环,来不停的消费队列中的Message对象
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // 当消息队列中没有任何消息了,退出循环
            return
        }
        // 省略一些trace相关的代码
        try {
            // 将消息分发给对应的target
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        }
        // 省略一些log等代码
        // message是复用的,一个消息分发完了,之后进行资源回收工作
        msg.recycleUnchecked();
    }
}

可以看到Looper.loop做的最核心的两件事:

  1. 驱动循环不停从MessageQueue中取Message。
  2. 将Message分发给Message.target。

Message等待被消费的数据

Message.target是什么呢?

我们要发送一个消息,调用的API都是:Hander.postXXX或者Handler.sendXXX系列的API。postXXX表示发送一个Runnable,前者postXXX系列API都是基于后者系列sendXXX实现的,postXXX发送出去的Runnable最终还是封装成了Message(Message.callback成员)。

Message的创建,在真正调用send系列API之前,大多都会有如下操作:

// 因为进程中可能会有大量消息的场景,为了实现对象复用
Message m = Message.obtain();
// obtain如下:
public static Message obtain() {
    // sPoolSync是一个Object,这里用作Lock
    synchronized (sPoolSync) {
        // 以链表的形式,存放所有的Message缓存
        // 需要的时候,再从链表头一个一个取。
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    // 如果队列中没有,再做重新创建,在一个Message被Looper.loop分发消费完了之后
    // 会进行相应的回收工作。
    return new Message();
}

最终,Message的发送,都是在sendMessageAtTime这个API:

// Message是要发送的消息对象,uptimeMillis表示这个消息需要被处理的时间
public boolean sendMessageAtTime(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(MessageQueue queue, Message msg, long uptimeMillis) {
    // 而这里的target,就是发送这个消息的Handler。 
    msg.target = this;
    // 如果Handler构造方法中设置了这个Handler发送的都是异步消息
    // 这里会将Message对象标记为是异步Message,作用后面再分析。
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

如上述代码,Handler将自己和所要传递的信息或者任务(Runnable)打包到了Message中,然后调用MessageQueue的入队列方法,将消息入队列。

那么这里清楚了,Message.target实际上就是Handler自己,Handler的dispatch方法如下:

public void dispatchMessage(Message msg) {
    // 如果是发送一个runnable,那么会调用runnable.run跑要执行的任务
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 如果在创建Handler的时候传入了callback字段
        // 那么消息的消费会被传递给callback
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 否则,调用等待子类重写的handleMessage,将消息
        // 交给Handler的具体子类去处理。
        handleMessage(msg);
    }
}