乐趣区

关于kotlin:Android中Handler的消息机制分析一

ps:浏览原文可获取 demo 源码,文章开端有原文链接

ps:源码是基于 android api 27 来剖析的,demo 是用 kotlin 语言写的。

Handler 是 Android 线程之间的音讯机制,次要的作用是将一个工作切换到指定的线程中去执行,与 Handler 一起协同工作的有 Looper、Message 和 MessageQueue;上面咱们以 Handler 在主线程解决音讯为例,对 Handler 发送音讯(实质上是将音讯插入链表)和解决音讯(顺便将音讯移除链表)相干的源码进行剖析。

咱们看一下 ActivityThread 中的 main 办法;

public static void main(String[] args) {

    ......
    //1、Looper.prepareMainLooper();
    ......
    //2、Looper.loop();
    ......

}

正文 1 其实它外部是初始化 Looper 和 MessageQueue,咱们点击正文 1 相干的代码看看;

public static void prepareMainLooper() {

    //3、prepare(false);
    ......

}

咱们点击进去看看正文 3 中的代码,也就是 Looper 的 prepare 办法;

private static void prepare(boolean quitAllowed) {

    if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");
    }
    
    //4、sThreadLocal.set(new Looper(quitAllowed));

}

从 Looper 的 prepare 办法看出,一个线程中只能实例化一个 Looper,也就是只能有一个 Looper 并将 Looper 保留到 ThreadLocal 类型的变量中;ThreadLocal 的作用是使同一变量在每一个线程中有各自的正本,每个线程用同一个 ThreadLocal 对象保留的数据是不共享的;咱们看看正文 4 中 Looper 的构造方法;

private Looper(boolean quitAllowed) {

    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();

}

从 Looper 的构造方法能够看出,这里顺便实例化了一个 MessageQueue 对象;咱们假如在子线程中发送一条音讯,用什么语句好呢?咱们就用 Handler.sendEmptyMessage 语句吧,看看 Handler 的 sendEmptyMessage 办法;

public final boolean sendEmptyMessage(int what)
{

    return sendEmptyMessageDelayed(what, 0);

}

sendEmptyMessage 办法调用了 Handler 的 sendEmptyMessageDelayed 办法,咱们看看 sendEmptyMessageDelayed 办法;

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {

    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);

}

sendEmptyMessageDelayed 办法里创立了一个 Message 对象,并调用了 Handler 的 sendMessageDelayed 办法,咱们往下看 sendMessageDelayed 办法;

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{

    if (delayMillis < 0) {delayMillis = 0;}
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

}

这里的 SystemClock.uptimeMillis() 语句示意零碎的开机工夫,sendMessageDelayed 办法调用了 Handler 的 sendMessageAtTime 办法,sendMessageAtTime 办法又调用了 Handler 的 enqueueMessage 办法,Handler 的 enqueueMessage 办法最终调用了 MessageQueue 的 enqueueMessage 办法,咱们且看 MessageQueue 的 enqueueMessage 办法;

boolean enqueueMessage(Message msg, long when) {

    ......
    synchronized (this) {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;

        //5、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.
            //6、needWake = mBlocked && p.target == null && msg.isAsynchronous();

            //7、Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {break;}

                //8、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.
        //9、if (needWake) {nativeWake(mPtr);
        }
    }
    return true;

}

正文 5 示意队列为空或者以后音讯不是提早音讯又或者以后音讯执行工夫小于队头音讯执行工夫;正文 6 中的 p.target == null && msg.isAsynchronous() 示意为这个音讯是零碎音讯;正文 7 示意依据这个音讯的执行工夫去将这个音讯插入到适当的地位;正文 8 示意如果队列中有零碎音讯,就不须要唤醒;正文 9 示意须要的状况下唤醒取音讯线程,也就是唤醒 Looper.loop 办法所在的线程;MessageQueue 的 enqueueMessage 办法最终实现音讯的插入。

在 ActivityThread 的 main 办法正文 1 中能够看到 Looper 的初始化是在主线程中进行的,正文 2 中的 Looper.loop 办法也是在主线程进行调用的,当咱们在 Activity 中间接初始化一个 Handler 时,Handler 的初始化也是在主线程执行的,这就阐明了 Handler 解决音讯的线程和 Handler 持有 Looper 的初始化时所在的线程是同一线程,什么意思呢?也就是创立 Looper 对象的线程和 Handler 解决音讯的线程是同一线程。

咱们看一下 ActivityThread 的 main 办法中正文 2 的代码;

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;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        //10、Message msg = queue.next(); // might block
        ......
        try {
            //11、msg.target.dispatchMessage(msg);
            ......
        } finally {......}
        ......
        //12、msg.recycleUnchecked();}

}

正文 10 的代码最终是获取音讯,咱们看一下正文 10 的具体实现,也就是 MessageQueue 的 next 办法;

Message next() {

    ......
    int nextPollTimeoutMillis = 0;
    for (;;) {
        ......
        //13、nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            ......
            if (msg != null) {if (now < msg.when) {
                    //14、// Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    //15、mBlocked = false;
                    if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}
                    //16、msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message:" + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                //17、nextPollTimeoutMillis = -1;
            }
            ......
        }
        ......
    }

}

正文 13 示意线程阻塞;正文 14 示意队头音讯没到执行工夫,那么就休眠 nextPollTimeoutMillis;正文 15 示意失去一个须要立刻执行的音讯;正文 16 示意将链表中的音讯进行移除;正文 17 示意队列中没有音讯了,那么就进入无限期休眠;MessageQueue 的 next 办法最终获取一个音讯并从链表中移除音讯。

咱们看 Looper 中 loop 办法的正文 12,它示意对 Message 对象进行回收,咱们要发送音讯的 Message 对象,尽量不要 new Message() 创立一个,最好的形式是通过 Message.obtain() 获取,它每次都会从可回收的对象池 (以链表的模式) 中获取一个 Message 对象,如果有的话,这样就防止了对象的创立,升高了内存的开销。

咱们看看正文 11 它是音讯的处理过程,msg.target 其实是一个 Handler,也就是咱们发送音讯的那个 Handler,如何证实呢?发送音讯的过程中会调用 Handler 的 enqueueMessage 办法,咱们看看 enqueueMessage 办法;

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

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

}

这会能够证实 msg.target 就是发送音讯的 Handler 了吧,咱们看一下 Handler 的 dispatchMessage 办法;

public void dispatchMessage(Message msg) {

    if (msg.callback != null) {handleCallback(msg);
    } else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}
        }
        handleMessage(msg);
    }

}

从 Handler 的 dispatchMessage 办法能够看出,解决音讯的形式有 3 种,第一种是 msg.callback,它示意一个 Runnable 接口,不须要重写 Handler 的 handleMessage 办法;第二种是 mCallback,它示意 Callback 接口,不须要重写 Handler 的 handleMessage 办法;第三种是 Handler,须要重写 handleMessage 办法;3 种解决音讯的优先级从大到小排列为:Runnable > Callback > Handler。

上面对这 3 种解决音讯的形式进行举例:

1)Runnable 接口解决形式:

class MyR1: Runnable {

    override fun run() {Log.d("MainActivity","MyR1 所在以后线程为:" + Thread.currentThread().name)
    }

}

mH1!!.post(MyR1())

2)Callback 接口解决形式:

class MyCallback: Handler.Callback {

    override fun handleMessage(msg: Message?): Boolean {Log.d("MainActivity","MyCallback 所在以后线程为:" + Thread.currentThread().name)
        return true
    }

}

mH2 = Handler(MyCallback())

3)Handler 子类解决形式:

class MyHandler: Handler() {

    override fun handleMessage(msg: Message?) {super.handleMessage(msg)
        Log.d("MainActivity","MyHandler 所在以后线程为:" + Thread.currentThread().name)
    }

}

mH3 = MyHandler()

从本篇文章剖析的过程中,Handler 的音讯发送过程是这样的:

Handler.sendEmptyMessage -> Handler.sendEmptyMessageDelayed -> Handler.sendMessageDelayed -> Handler.sendMessageAtTime -> Handler.enqueueMessage -> MessageQueue.enqueueMessage -> 音讯插入链表

Looper 的 loop 办法解决音讯的过程是这样的:

Looper.loop -> MessageQueue.next -> 链表中取出音讯 -> Handler.dispatchMessage(Runnable 接口解决形式、Callback 接口解决形式、Handler 子类解决形式)

退出移动版