Handler浅析

88 阅读10分钟

Handler 是Android最常用的消息机制,很多地方都在使用,如四大组件的创建和使用,view的渲染机制等等,我们平时开发过程中也会经常用到,使用Handler发送一个消息后,他会按照消息的时间,按照顺序来进行分发,分析Handler可以从两个方向解析,1、Java方面 2、native方面。

首先先从Java方面开始解析。

Java层面分析

常用类分析

ThreadLocal

用于保存Looper对象,内部是用Thread线程对象来保存Looper的,也就是说,一个线程只能有一个Looper,一个Looper有一个MessageQueue,但是可以有多个Handler 在Looper中是这样定义ThreadLocal的

Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal内部有几个核心方法如下

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

因为Looper的创建是在当前线程中,所以在子线程中,是没有默认创建Looper的,所以在子线程中调用Looper.myLooper()返回的是null

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

Looper

Looper负责将MessageQueue中的消息通过loop()方法来取出来, 构造函数创建了MessageQueue对象

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

内部还有几个常用的方法

正常创建Looper quitAllowed代表是否可以安全退出 创建Looper后是保存到ThreadLocal中,这个后续分析。

正常创建Looper quitAllowed代表是否可以安全退出
创建Looper后是保存到ThreadLocal中,这个后续分析
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));
}

在ActivityThread的main()方法中调用的这个方法,即在应用启动后,就会创建一个主线程的Looper
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

loop()方法,是核心方法,通过死循环的方式来不断的从MessageQueue队列中获取message消息。最后走到了MessageQueue的next()方法中。

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();
    }
}

Message

Message是消息的数据模型,是以链表的形式存在,内部有几个关键的参数,如下

public final class Message implements Parcelable {
    public int what;        消息的标识
    public Object obj;      消息的数据
    Handler target;         表示是哪一个Handler所属的消息
    Runnable callback;      消息内部的回调方法
    Message next;           以链表的形式存储,下一个
    int flags;              用来标识是否是异步消息,不受同步屏障的的影响消息
}

Message内部的核心方法 obtain()方法表示从内部sPoolSync消息池中获取消息。用来复用,避免重复创建

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

recycle()方法是回收Message的方法,如果消息池中的数量小于最大消息数量50的时候,就可以往消息池中继续添加消息。

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = UID_NONE;
    workSourceUid = UID_NONE;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

setAsynchronous方法表示是否是一个异步消息,后面存储和获取是会结合同步屏障来处理。

public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}

Handler

发送消息的源头,我们来看一下通常的使用方法: 发送一个空消息,和post一个延迟的消息,runnable中执行具体的方法。

val handler = Handler(Looper.getMainLooper())
handler.sendEmptyMessage(1)

Handler(Looper.getMainLooper()).post({
    
})

Handler(Looper.getMainLooper(),object : Handler.Callback{
    override fun handleMessage(msg: Message): Boolean {
        return false
    }
})

我们开始跟踪一下内部的实现

1.向MessageQueue中发送消息
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

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

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);
}

分别的作用是,从线程池中获取一个空的Message,然后把当前的时间和delay时间算好后,作为when,作为后面插入到MessageQueue有序队列中的依据。然后target标记当前Handler。如果是异步消息则设置上,不过正常的消息都是同步消息。

最后交给了MessageQueue来进行添加Message。

2.Handler接受消息

进行消息的方法,其中有三种情况

  1. 消息本身的callback,即上面的post
  2. callBack 在构造函数中传入callback
  3. 继承Handler 重写handleMessage方法
public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

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

public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    boolean handleMessage(@NonNull Message msg);
}

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(@NonNull Message msg) {
}

MessageQueue

向MessageQueue中存消息和取消息都是在MessageQueue方法中,下面继续分析一下。

存消息时,1.如果没有头节点,或者when为0 或者当前消息的时间小于Message队列中的头节点的消息的时间,则将当前这个message插入到头节点,然后设置needWake=true,来通知looper进行取数据。2.如果当前消息并不小于头节点的消息。 3.判断如果头节点是同步屏障,且当前消息是异步消息,则需要唤醒looper。4.根据时间来插入消息。

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.");
        }


        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //1.如果没有头节点,或者when为0 或者当前消息的时间小于Message队列中的头节点的消息的时间,则将当前这个message插入到头节点,然后设置needWake=true,来通知looper进行取数据。
        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 {
            //2.如果当前消息并不小于头节点的消息
            //3.判断如果头节点是同步屏障,且当前消息是异步消息,则需要唤醒looper
            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;
        }
        
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

读取消息

  1. 首先使用epoll机制来阻塞线程
  2. 如果头节点消息是同步屏障,则获取同步屏障后的异步消息
  3. 如果消息的时间大于当前时间,则记录需要唤醒的延迟时间
  4. 链表常规操作,将当前msg取出
  5. IdleHandler 的消息处理
Message next() {
    ...
    for (;;) {
        //1.首先使用epoll机制来阻塞线程
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
           
            Message prevMsg = null;
            Message msg = mMessages;
            //2.如果头节点消息是同步屏障,则获取同步屏障后的异步消息
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                //3.如果消息的时间大于当前时间,则记录需要唤醒的延迟时间
                if (now < msg.when) {
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    //4.链表常规操作,将当前msg取出
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                nextPollTimeoutMillis = -1;
            }

            
            //5.IdleHandler 的消息处理
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        nextPollTimeoutMillis = 0;
    }
}

同步屏障

Handler 同步屏障和异步消息

在MessageQueue中的postSyncBarrier()方法进行发布一个同步屏障消息。主要是在消息列表的头节点前插入一个Message,但是没有使用enqueueMessage方法来进行插入,而是在头节点前直接插入,此时这个消息的target为空。

private int postSyncBarrier(long when) {
    synchronized (this) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;

        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}

接下来看一下ViewRootImpl中的scheduleTraversals()方法中是如何发布一个同步屏障的,在Vsync信号接收到后,开始渲染上一桢的view时,会回调到scheduleTraversals()方法。

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
  1. 首先调用postSyncBarrier()来发一个同步屏障。
  2. 然后调用Choreographer的postCallback()方法来发送一个异步消息,setAsynchronous(true);来设置异步消息
  3. 在接受到消息的回调后,需要及时的移除同步屏障,否则就不会再接受其他的同步消息了。移除后,再判断是否要唤醒looper
private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {

    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            scheduleFrameLocked(now);
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}
public void removeSyncBarrier(int token) {
    synchronized (this) {
        Message prev = null;
        Message p = mMessages;
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        final boolean needWake;
        if (prev != null) {
            prev.next = p.next;
            needWake = false;
        } else {
            mMessages = p.next;
            needWake = mMessages == null || mMessages.target != null;
        }
        p.recycleUnchecked();

        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}

Native层面

native层面主要分析 nativeinit nativePollOnce 和 nativeWake 唤醒和阻塞两个方法,其中native方法的调用是在MessageQueue中。

MessageQueue.java
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
@UnsupportedAppUsage
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

1. nativeinit()

nativeinit()方法是在MessageQueue的构造方法中调用。

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

那继续跟踪native层的代码

frameworks/base/core/jni/android_os_MessageQueue.cpp

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }

    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}

创建了NativeMessageQueue native层的MessageQueue消息队列 并将NativeMessageQueue对应的指针mPtr返回给Java层的MessageQueue中,以便后面的nativePollOnce使用 继续看一下NativeMessageQueue frameworks/base/core/jni/android_os_MessageQueue.cpp

NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

如果当前线程下有Looper就直接返回,没有的话就创建一个,也是一个线程有一个Looper。 创建了一个native层的Looper,跟Java层的没有什么关系,而且读取操作都放在了Looper这个类当中,跟Java的不一样,Java的都放在了MessageQueue中。

Looper::Looper(bool allowNonCallbacks)
    : mAllowNonCallbacks(allowNonCallbacks),
      mSendingMessage(false),
      mPolling(false),
      mEpollRebuildRequired(false),
      mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
      mResponseIndex(0),
      mNextMessageUptime(LLONG_MAX) {
    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));

    rebuildEpollLocked();
}

Looper中主要做了两件事情:

  1. 构造唤醒事件的fd(文件操作符)mWakeEventFd
  2. 重建epoll机制
总结一下nativeinit方法
  1. 创建NativeMessageQueue,进而创建Looper。
  2. 构造唤醒事件的fd 文件操作符mWakeFd和重建epoll机制,即通过管道和epoll机制创建一套消息机制
  3. 将NativeMessageQueue对象转换成Long类型存储到Java层的MessageQueue的mPtr中

2.nativePollOnce()

Java层Looper.loop() 调用MessageQueue.next()方法时,会调用nativePollOnce()

//MessageQueue.java
 Message next() {
    for(;;){
         nativePollOnce(ptr, nextPollTimeoutMillis);
    }
 }

当没有消息时,nativePollOnce会进入阻塞

nativePollOnce方法总结
  1. 将ptr转化成NativieMessageQueue,并调用其pollOnce方法,最后会调用到Looper的pollInner方法
  2. Looper的pollInner方法是一个阻塞方法,内部有epoll_wait方法,如果没有消息时会阻塞,并释放cpu资源,epoll_wait方法会监听创建的epoll文件操作符所监听的事件,如果发生变化,会从管道中读取放入到事件集合中,如果没有事件,epoll_wait方法会让当前线程进入休眠。
  3. 读取到消息后,会通过native层的Handler发送一个Message消息。处理完后会返回到Java层,

3.nativeWake()

Java层的MessageQueue.enqueueMessage方法调用时,会判断是否需要调用nativeWake(),当有新的消息存入时,会调用nativeWake来通知Looper进行读操作。

//frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
  NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
  nativeMessageQueue->wake();
}
//frameworks/base/core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::wake() {
   mLooper->wake();
}
//system/core/libutils/Looper.cpp
void Looper::wake() {
    uint64_t inc = 1;
    //使用write函数通过mWakeEventFd往管道写入字符inc
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    //...
}

最后调用到native层的Looper.wake方法。通过write函数,通过mWakeEventFd往管道中写入字符inc,然后我们在nativeInit方法中创建的mWakeEventFd文件操作符,会监听是否有可读事件,如果有的话,就会让epoll_wait方法从管道中读取事件并返回。

参考链接: www.jianshu.com/p/ac3cd5a19… www.jianshu.com/p/57a426b8f…