Handler处理消息相关源码解析

一、handler的用法

1.1、用于线程切换

Handler handler = new Handler(Looper.getMainLooper());
new Thread(new Runnable() {
    @Override
    public void run() {
        handler.post(() -> {
            binding.tv.setText("hello world");
        });
    }
}).start();

1.2、线程间通信

为了防止handler导致内存泄露,handler对Activity采用弱引用,因弱引用不会影响Activity对象生命周期。

static class MyHandler extends Handler {
    WeakReference mActivityWeakReference;、
    public MyHandler(MainActivity activity) {
        mActivityWeakReference = new WeakReference<>(activity);
    }
    @Override
    public void handleMessage(@NonNull Message msg) {
        MainActivity activity = mActivityWeakReference.get();
        if (activity != null && msg!=null && msg.obj!=null) {
             //todo 
        }
    }
}
MyHandler myHandler = new MyHandler(MainActivity.this);
new Thread(new Runnable() {
    @Override
    public void run() {
        Message message = Message.obtain();
        message.obj = "aaa";
        mMyHandler.sendMessage(message);
    }
}).start();

1.3、任务延时执行

static class MyRunnalbe implements Runnable {
    @Override
    public void run() {
        Log.e("test", "test memory leak?");
    }
}
//发送延时任务
Handler handler3 = new Handler(Looper.getMainLooper());
handler3.postDelayed(new MyRunnalbe(), 10000);

最后大家不要忘了,一定记得在Activity的onDestroy及时解除Message对Handler的强引用。

@Override
protected void onDestroy() {
    super.onDestroy();
    mMyHandler.removeCallbacksAndMessages(null);
}

二、Handler(Loope(MessageQueue))的创建

2.1、Handler创建

先看看handler的构造函数。

构造函数肯定是相互调用,只需要看最多的参数的那个

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) { xxxx }

  • 从构造可以看出,Handler的创建需要Looper,没有Looper玩不转呐,我们之前写handler用法的时候,都是在主线程创建Handler,它构造里面会调用Looper.myLoop()获取Looper。

  • ThreadLocal可用于线程间的数据隔离,这是解决多线程并发的一种手段 (不共享可变资源)。将线程需要的Looper保存在ThreadLocal里面。

  • 我们App的入口函数就是ActivityThread的main方法,一般在main方法里面就做两件事,第一件就是启动创建主线程的Looper,启动Looper轮询;第二件事就是将ApplicationThread的binder对象注册给AMS(AMS拿到它后就可以调用App的方法)。loop轮询一旦跳出了死循环,App直接就抛异常了。

  • 如果最后一个参数async为true,那么通过handler.sendMessage的message都会打上异步消息的标签。

//from Handler
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()");
    }
  .......
}

//from Looper
static final ThreadLocal sThreadLocal = new ThreadLocal();
//myLooper直接从Threadlocal中取
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
//创建主线程Looper,因为主线程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();
    }
}
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));
}

//from ActivityThread
public static void main(String[] args) {
    ....... 
    //创建主线程Looper
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    //向AMS注册,将app的binder对象给AMS
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    .....
    //启动轮询
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

Q: 如果直接在子线程里面new Handler()创建,从ThreadLocals里面肯定是取不到对应的looper的,所以会抛异常。

需要显式调用Looper.prepare()才行。

子线程中弹吐司也要先通过Looper.prepare()创建Looper,不然也会奔溃。

原因同上


2.1.1、ThreadLocal原理

都说ThreadLocal可用于线程间数据隔离,完美的解决并发问题,那啥原理呢? 先看看用法。
public void set(T value) {
    Thread t = Thread.currentThread(); //获取当前线程
    ThreadLocalMap map = t.threadLocals; //线程的t.threadLocals
    if (map != null)
        map.set(this, value);  //直接添加
    else
        createMap(t, value);   //那就给当前线程创建这个ThreadLocalMap类型的t.threadLocals成员
}
private void set(ThreadLocal key, Object value) {
    Entry[] tab = table;
    int len = tab.length; 
    int i = key.threadLocalHashCode & (len-1); //斐波那契散列算法
    for (Entry e = tab[i]; e != null;e = tab[i = nextIndex(i, len)]) { //开放寻址法解决冲突
        ThreadLocal k = e.get();    
        if (k == key) {
            e.value = value;
            return;
        }
        if (k == null) {
            replaceStaleEntry(key, value, i); 
            return;
        }
    }
    tab[i] = new Entry(key, value); //key就是ThreadLocal
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)  rehash();
}
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}

threadlocal.set(xxx)时,会从当前线程(主线程/子线程)取出它的ThreadLocalMap类型的成员threadLocals,不为null就直接添加了,为空则先创建。ThreadLocalMap底层采用数组实现,不像HashMap采用数组+链表/红黑树实现。这里的元素的index采用斐波那契散列算法实现,采用开放寻址法解决散列冲突,具体参考ThreadLocalMap的hash算法。数组元素Entry很有特点。

static class Entry extends WeakReference> {
    Object value;
    Entry(ThreadLocal k, Object v) {
        super(k);
        value = v;
    }
}

Entry是一个弱引用类,直接通过entry.get()可以获取到Threadlocal引用,因其作为entry的key,只能对应一个值value。同时也参与table数组index的hash算法(i = key.threadLocalHashCode & (len-1))。如通过entry.get()获取threadLocal为null时,此时value不为null,就有发生泄漏可能,一般建议就是用完及时进行remove掉。

2.2、Looper的创建

public static void prepare() {
    prepare(true);
}
public static void prepare() {
    prepare(true);
}
//quitAllowed 是否支持退出,默认是支持退出的。但是主线程的looper不支持退出。
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));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();

Handler创建需要Looper,Looper创建需要MessageQueue。如果多次调用Loopr.prepare来创建Looper会报错,即在一个线程里面创建多个Looper会报错

主线程的Looper当然不支持退出,不然随便调用looper.quit,App就奔溃了。用户子线程中通过Looper.prepare()创建Looper默认是true,支持退出的。

2.3、MessageQueue的创建

private long mPtr; // used by native code
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

MessageQueue创建时,创建了一个long类型的成员,native可以通过这个long指针直接强转成native对象用,这里是底层的NativeMessageQueue的指针

从这里可以看出,一个线程可以创建无数个Handler,但只能有一个Looper,一个MessageQueue

三、Looper.loop() 与 handler.sendMessage(xx)

3.1、开启轮询消息Looper.loop()

入口函数ActivityThread.main()方法中,就开启了Looper.loop()。

//from Looper
public static void loop() {
    ......
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // 可能阻塞
        ......
        msg.target.dispatchMessage(msg);
       ...... 
        msg.recycleUnchecked(); //消息进入循环池
    }
}

Q: 如果让我们写个Handler分发、处理Message,你会怎么写?
因为Handler的分发事件可能在其他线程,被称为生产者,处理Message的Handler被称为消费者,典型的生产消费模型,肯定会考虑生产多了+消费慢,生产少了+消费快的场景,此时伪代码如下:

public synchronized void loop() {
    long now = System.currentTimeMillis();
    int count;                                   //mMessageQueue.top()就是获得一个Message
    while ((count = mMessageQueue.size()) == 0 || mMessageQueue.top().when > now) {
        long diff = (count == 0) ? 0 : (now - mMessageQueue.top().when);
        try {
            this.wait(diff);           //这里就类似Message msg = queue.next();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    ......
    this.notifyAll();
}

系统肯定不会用wait/notify+synchronized这种性能欠佳的组合实现,对的,底层用的是Epoll机制实现
来看看MessageQueue的Next方法。

Message next() {
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();  //被调用说明后面的代码可能会引起线程阻塞(此处不懂)
        }
        nativePollOnce(ptr, nextPollTimeoutMillis)  //epoll_wait
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            /此处msg没有target(handler),那就表示这个msg为同步屏障消息,配合异步消息使用的
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;    
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //获取需要等待的时间
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                     //终于拿到一个可用的Message了,单链表取出Message的操作,模板代码
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;  //所以mMessage就是下一个要处理的Message。记住了
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                nextPollTimeoutMillis = -1;  //-1传给native的epoll_wait,表示永远不能唤醒,需要主动被wake
            }
              //messageQueue为empty了 或者 mMessage是将来才处理的(when还没到)
            if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                mBlocked = true;   //那就继续阻塞
                continue;
            }
            //每次处理的handler最多只能是4个,多了不处理????
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler
            boolean keep = idler.queueIdle(); //idleHandler的queueIdle返回值true/false处理不同
            if (!keep) {                      
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        pendingIdleHandlerCount = 0;
        nextPollTimeoutMillis = 0;  //处理完idle消息后,立马就唤醒了,接下来再取新的。
    }
}

nextPollTimeoutMillis(阻塞的超时时间)有两种情况

  • 第一种:首次启动loop,nextPollTimeoutMillis = 0,进入到nativePollOnce(ptr, nextPollTimeoutMillis) 这里,其底层epoll_wait(epFd,events,MAX_EVENTS,timeout)阻塞,这个阻塞可因超时时间到了或者关注的事件可读了被唤醒,这里timeout为0,所以很快会被唤醒。

  • 第二种:非首次,nextPollTimeoutMillis就来自 (msg.when-now),epoll_wait的超时时间就可能不是0了,时间未到,就阻塞着,直到被唤醒。

3.1.1、nativePollOnce(ptr, nextPollTimeoutMillis)的底层调用

看源码C++代码可以使用官方网站:Android Code Search

private native void nativePollOnce(long ptr, int timeoutMillis); 

之前说java层创建MessageQueue的时候,native层返回一个NativeMessageQueue的指针,NativeMessageQueue的创建伴随着native层的Looper的创建。

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); //源码是CPP所以new的
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }
    nativeMessageQueue->incStrong(env);
    return reinterpret_cast(nativeMessageQueue); //long类型的指针
}
//NativeMessageQueue的构造函数:通过参数化表进行构造
NativeMessageQueue::NativeMessageQueue() :mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);  //创建了一个native层Looper,给native线程绑定了一个Looper
        Looper::setForThread(mLooper);
    }
}

nativePollOnce的调用,发现java层的long类型指针强转成nativeMessageQueue,这是常规操作,java层传递过来的timeoutMillis一路携带。

//nativePollOnce方法
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {  //java的long类型指针这里又强转成nativeMessageQueue
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
   ....
    mLooper->pollOnce(timeoutMillis);
  ......
}
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
        ......
        //与handler功能关系不大的逻辑略去,这里是用底层Looper干其他的事情。
        ......
        result = pollInner(timeoutMillis);
    }
}

下面的方法是重点 epoll_wait出来了。

//timeoutMillis一路携带
int Looper::pollInner(int timeoutMillis) {
    // Adjust the timeout based on when the next message is due.
    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
        if (messageTimeoutMillis >= 0
                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
            timeoutMillis = messageTimeoutMillis;
        }
    }
   .....
    int result = POLL_WAKE;
    mPolling = true;
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];  //经典的Epoll机制
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    mPolling = false;
    .....
    for (int i = 0; i < eventCount; i++) {
        const SequenceNumber seq = eventItems[i].data.u64;
        uint32_t epollEvents = eventItems[i].events;
        if (seq == WAKE_EVENT_FD_SEQ) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
           ......//处理其他非handler的功能
        }
    }
    return result;
}
void Looper::awoken() {
    uint64_t counter;  
    //及时从管道mWakeEventFd.get()读描述符中读取数据
    TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}

3.1.1.1、为了理解这里的调用可以看个管道配合Epoll的例子 具体来自这个博客

早期版本4.4及其以下版本,handler底层用的管道+Epoll实现,现在采用eventfd()+Epoll机制实现。

..... //Mac上用CLion跑demo发现Mac找不到epoll的库
int main(){ 
    int ret,pid;
    int pipe_fd[2];          
    if((ret=pipe(pipe_fd))<0) {     //调用linux的pipe方法,获取读写描述符,读0 写1
        return -1;
    }
    const int MAXEVENTS = 1024;  
    struct epoll_event events[MAXEVENTS];  //就绪列表   
    struct epoll_event ev;             //epoll_event事件                
    ev.data.fd = pipe_fd[0];          //读描述
    ev.events = EPOLLIN|EPOLLET;  //EPOLLIN事件触发表示对应的文件描述符上有可读数据。EPOLLET边缘触发 
    int epfd=epoll_create(MAXEVENTS);  //创建epoll的句柄,size(MAXEVENTS)用来告知内核监听的数目
    ret=epoll_ctl(epfd,EPOLL_CTL_ADD,pipe_fd[0],&ev);//注册要监听的事件,pipe_fd[0]再次传入。
    if (ret != 0){
        close(pipe_fd[0]);
        close(pipe_fd[1]);
        close(epfd);
        return -1;
    }
    //fork()有意思:会返回两次分别在父子进程返回,pid>0表示的是父进程,返回的pid==0表示子进程,
    if((pid=fork())>0) {  
        int count=epoll_wait(epfd,events,MAXEVENTS,5000); 
        char r_buf[100];
        for(int i=0;i

epoll的三个关键函数epoll_create返回epollFd,epoll_ctl注册要监听的事件,epoll_wait阻塞等待关注的事件到来。

epoll_wait函数说明

  • 返回值:成功时返回就绪列表中文件描述符的个数,超时返回0,异常返回-1。
  • timeout:指定epoll的超时时间,单位是毫秒。当timeout为-1是,epoll_wait调用将永远阻塞,直到某个事件发生。当timeout为0时,epoll_wait调用将立即返回。
  • maxevents:指定最多监听多少个事件,必须大于0。
  • events:检测到事件,将所有就绪的事件从内核事件表中复制到它的第二个参数events指向的数组中。
3.1.1.2、mWakeEventFd

Looper创建时会创建Epoll实例,然后注册 mWakeEventFd。

//Looper创建
Looper::Looper(bool allowNonCallbacks):.....{
    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
    AutoMutex _l(mLock);
    if (mEpollFd >= 0) {
        mEpollFd.reset();
    }
    // Allocate the new epoll instance and register the WakeEventFd.
    mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
    epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
   ......
}
//捕捉一下WakeEventFd的操作
using unique_fd = unique_fd_impl;
android::base::unique_fd mWakeEventFd;
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
void Looper::wake() {
    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
   ......
}
void Looper::awoken() {
    uint64_t counter;  
    TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}

之前例子采用管道读写,通过pipe方法实现,轻易获得读、写描述符,读写描述符分开的,而这里读写都是mWakeEventFd.get(),通过eventfd函数创建fd。
什么是eventfd
eventfd是Linux 2.6提供的一种系统调用,它可以用来实现事件通知。eventfd包含一个由内核维护的64位无符号整型计数器,创建eventfd时会返回一个文件描述符,进程可以通过对这个文件描述符进行read/write来读取/改变计数器的值,从而实现进程间通信。(mac完全用不了这个epoll和eventfd,尴尬)
EPOLLIN的触发条件是内核接收到新发来的数据,从不可读状态变为可读状态。

一旦我们 nativePollOnce(ptr, nextPollTimeoutMillis)经过底层走一圈,混到timeout时间到了,epoll_wait返回,不阻塞了,就进入java层做取消息的操作

因java取消息它是优先判断链表头是不是同步屏障,所以接下来先看同步屏障消息和异步消息。

3.1.2、同步屏障消息和异步消息

Message的三种类型 普通消息、同步屏障、异步消息,区分它们很简单。

  • 普通消息 我们一般发送的Message默认就是,其target就是handler。
  • 同步屏障 因没有message.target,所以不需要分发,(正常发送的消息必须指定target),没有handler的消息就可以认定为同步屏障。它也有时间戳,只会影响后面的消息。消息队列可以插入多个同步屏障,插入同步屏障没有唤醒线程处理消息,普通消息插入会唤醒。插入同步屏障的会返回token(序列号),作为remvoe同步屏障方法的参数用。
  • 异步消息 设置过msg.setAsynchronous(true)的Message。
 //MessageQueue的next方法部分逻辑  被唤醒后,优先判断是否同步屏障+异步消息组合
 if (msg != null && msg.target == null) {
    do {
        prevMsg = msg;
        msg = msg.next;    
    } while (msg != null && !msg.isAsynchronous());
 }

如果链表头(msg != null && msg.target == null)是同步屏障消息,那就去找下一个异步消息,二者狼狈为奸,阻挡正常普通消息的处理,此时普通消息就算达到when了,也只能乖乖等着。一般同步屏障+异步消息用于UI绘制这种需要立即响应的场景。

3.1.1.1、系统中使用同步屏障的场景
//当我们更新Ui时喜欢调用:
binding.tv.requestLayout();
    // ||||其调用路径||||
    view->requestLayout()
       viewRootImpl->requestLayout()
           viewRootImpl->scheduleTraversals()
-----------------------------------------------------------------------------
//from viewRootImpl
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;        //标记位为true
                                           //发送同步屏障消息
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                                          //编舞者添加监听
        mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        ......
    }
} 

//from MessageQueue
//插入同步屏障消息到链表,不走handler插入,直接在MessagQueue里面插入
private int postSyncBarrier() {
   long when = SystemClock.uptimeMillis();
    synchronized (this) {
        final int token = mNextBarrierToken++;   
        final Message msg = Message.obtain();    //没有设置target操作
        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) {     // 插入同步消息
            msg.next = p;
            prev.next = msg;
        } else {                //插入到链表头
            msg.next = p;
            mMessages = msg;
        }
        return token;         //同步屏障的token,用于取消用
    }
}

当我们调用View的requestLayout时,没有立马绘制,而是由ViewRootImpl来协助走绘制流程,先发送同步屏障消息,注册Choreographer监听(用户添加监听还可用于屏幕帧率获取,固定脉冲频率60Hz或者120Hz,它向SurfaceFlinger发起请求,获取脉冲Vsync的到来),脉冲信号到来的回调-->FrameDisplayEventReceiver的onVsync()。

//from Choreographer
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
    @Override
    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
        ........
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        Message msg = Message.obtain(mHandler, this); //this就是的run方法
        msg.setAsynchronous(true);  //发送异步消息
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
    @Override
    public void run() {
        mHavePendingVsync = false;
        doFrame(mTimestampNanos, mFrame);
    }
}
void doFrame(long frameTimeNanos, int frame) {
    final long startNanos;
    synchronized (mLock) {
        long intendedFrameTimeNanos = frameTimeNanos;
        startNanos = System.nanoTime(); //当前时间跟脉冲时间diff
        final long jitterNanos = startNanos - frameTimeNanos;
        if (jitterNanos >= mFrameIntervalNanos) { 
            final long skippedFrames = jitterNanos / mFrameIntervalNanos;
            if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {  //这个日志太常见--丢帧提示
                Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                        + "The application may be doing too much work on its main thread.");
            }
            final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
            frameTimeNanos = startNanos - lastFrameOffset;
        }
        if (frameTimeNanos < mLastFrameTimeNanos) {
            //请求下一个垂直同步信号
            scheduleVsyncLocked();
            return;
        }
       ......
        mLastFrameTimeNanos = frameTimeNanos;
    }
    ......
    //Choreographer的一些回调
   doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
   doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
   doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
}

// frmo ViewRootImpl 前面mChoreographer.postCallbac设置的回调转到mTraversalRunnable
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;   
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //移除同步屏障消息
        performTraversals();  //接下里就是测量、布局、绘制了
    }
}

同步屏障插入了,脉冲信号来临时,onVsync回调异步消息插入,当从消息队列里面取出消息时,如果同步屏障消息在链表头,那么插入的异步消息会很快得到处理,在真正开始执行绘制前,移除同步屏障。

如果消息队列为空 or 也没有同步消息 or 待处理的普通消息还没有到时间,那就该处理IdleHandler了

3.1.3、IdleHandler的处理

  • 处理时机 当MessageQueue为空或者接下来的Message的when还没有到的时候。
  • 使用场景 可用于启动优化,针对一些不是启动必须的操作,可以放到首页idleHandler中做延迟处理。
  • idler.queueIdle()的返回值 默认是false,就只处理一次后就移除了,如果返回true,可能会处理多次。

看来确实只有在目前没有可处理的Message了才会考虑处理idleHandler。

3.2、发送消息之handler.sendMessage(xx)

//from Handler
public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postDelayed(Runnable r, long delayMillis){
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0)  delayMillis = 0;
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this; //这个this就是Handler
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

用户使用的各种post/postDalay/sendMessage最终都是会走到enqueueMessage方法,在这里给Message设置target,target就是Handler。如果handler构造参数asynchronous传入true,就在这里给Message添加异步标志。

boolean enqueueMessage(Message msg, long when) {
   .....
   //考虑多线程,这里用了 synchronized
    synchronized (this) {
        ......
        msg.markInUse();
        msg.when = when;
        Message p = mMessages; //mMessages表示接下来要处理的Message,可能是null
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;          
            mMessages = msg;
            needWake = mBlocked;  //前面处在nativePollOnce的epoll_wait那就阻塞了为true
        } else {
            //要插入的消息比接下来要处理的消息(mMessages)的when还晚
            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;
}

在这里传入的msg不可能是同步屏障消息,因同步屏障消息直接通过postSyncBarrier插到队列里面。(注意postSyncBarrier是私有api,我们用不了,除了反射)

  • if (p == null || when == 0 || when < p.when) 表示链表头为null或要处理的比链表头mMessages还要早,那肯定要将msg插入到链表头了。一般mBlocked为true,然后就唤醒了。
  • needWake = mBlocked && p.target == null && msg.isAsynchronous() 能走到这里就说明插入的消息处理时间比队列头的msg晚,一般是不唤醒的,但是有特殊情况(链表头为同步屏障+首个异步消息插入)。
    假设当前mBlocked是true,队头的mMessages是同步屏障,要插入的消息为异步消息,neekWake此时为true。
    • 1、进入循环,同步屏障消息在链表头且队列中无异步消息,如果要插入的消息是异步消息,就会被唤醒。
    • 2、进入循环,同步屏障消息在链表头且队列中有异步消息,即使再插入消息,也不会唤醒。

总结一下插入消息能被唤醒的场景
1、链表头为null 或者 插入的普通消息/异步消息比链表头消息的处理时间早。
2、链表头为同步屏障+首个异步消息插入。

如当前链表头是同步屏障消息,插入的异步消息按时间处理顺序即使插入到队列中很靠后的位置,也不影响异步消息的处理时效,仍是优先处理的。

//loop取消息时,如果链表头是同步屏障消息,优先从队列里面找异步消息处理。
 ......
 nativePollOnce(ptr, nextPollTimeoutMillis)
    synchronized (this) {
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        Message msg = mMessages;
        //看到msg没有target(handler),那就表示这个Message为同步屏障消息,配合异步消息使用
        if (msg != null && msg.target == null) {
            do {
                prevMsg = msg;
                msg = msg.next;    
            } while (msg != null && !msg.isAsynchronous());
        }
        ......
        //处理普通消息
        ......

3.2.1、nativeWake(mPtr)

//from java MessageQueue
private native static void nativeWake(long ptr);
//from NativeMessageQueue
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);
    nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
    mLooper->wake();
}

// from native的Looper
void Looper::wake() {
    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
    .....
}

nativeWake是在mWakeEventFd.get()里面写入了unit64_t的1。mWakeEventFd.get()的eventfd包含一个由内核维护的64位无符号整型计数器,进程可以通过对这个文件描述符进行read/write来读取/改变计数器的值。当这里写入数值时,那epoll_wait就能返回了,这里的inc改成其他值应该也可以,因为我们只关心epoll_wait是否返回不再阻塞。
总结一下:epoll_wait在timeout超时时间到来时或mWakeEventFd.get()有数据可读时会返回,不再阻塞。

3.3、处理消息之handleMessage

用户可能用3种方式去处理消息

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {  //1
        handleCallback(msg);   
    } else {
        if (mCallback != null) {  //2
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);  //3
    }
}

Message在获取时,有传入callback就走1,handler构造函数传入了Callback,就走2,默认(mCallback.handleMessage(msg))的值为false,false表示我们还可以继续处理3,如果是返回值为true,一旦被处理过了,那我们重写的handleMessage方法还是没法调用。

四、关于Handler的其他问题

4.1、一般我们固定频率的任务(定时器)怎么实现比较好呢?

  • 方式一、handler.postDelayed,但是精度么呵呵呵,handler发送普通的延时消息,通过epoll_wait的超时机制实现,而且前面有Message如果处理不及时会导致精度更差了。
  • 方式二、Timer:底层通过synchronize+wait(diff)实现。
  • 方式三、AlarmManager,用的少。
  • 方式四:固定频率的线程池执行任务。
  • 方式五:Choreographer添加监听实现,频繁获取帧率可用它,相当于16ms的定时器。不知道有没得兄弟在实际场景中用过。

4.2、主线程Looper为啥没有ANR?

入口函数ActivityThread的main中开启了Loop.loop(),在queue.next()获取下一个Message时会走底层的epoll机制,进行阻塞等到超时时间到来时唤醒,就跟我们用synchronize+wait(diff)类似,wait是线程阻塞。
ANR机制 ANR大家都知道是应用程序无响应,而且这个应用程序无响应的弹窗不是在我们app进程,而是在AMS所在的进程(SystemServer进程)。
Service TimeOut: 前台20s,后台服务200s。
广播 Timeout: 前台广播10s,后台广播60s
contentProvider Timeout: 10s
InputDispatching Timeout: 5s
这里的超时,前面3大组件都是启动创建超时导致ANR,最后一个是用户操作导致ANR。

先就从最简单的Service启动看起吧。

//通过startService启动service
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);

//from ContextImpl 很关键的一个类,四大组件中只要attach上下文就需要它
@Override
public ComponentName startService(Intent service) {
    .......
    return startServiceCommon(service, false, mUser);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground,UserHandle user) {
        ...... //binder调用来了
        ComponentName cn = ActivityManager.getService().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), requireForeground,
                        getOpPackageName(), user.getIdentifier());
        ........
        return cn;
}
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

我们的app能够调用AMS的方法,肯定要先拿到AMS的binder代理对象,这里通过ActivityManager.getService()就可以搞到,发起binder调用AMS的startService。(AMS是在SystemServer进程,跟我们app不在同一个进程,看到源码里面以I开头的类,基本就当做binder对象类处理),同样AMS想要调用我们app的方法,也要通过binder调用,也要搞到我们app的binder代理对象(IApplicationThread)才行。
AMS有啥用,四大组件的创建启动都归他管,我们可以通过Instrumentation去捕捉四大组件的生命周期方法。

//from AMS
AMS-->startService(XXX)
    AMS--->startServiceLocked(XXX)
        AMS--->startServiceInnerLocked(XXX)
           AMS--->bringUpServiceLocked(XXX)
                 AMS--->realStartServiceLocked(XXX)
                 
private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
  //创建前埋入炸弹
    bumpServiceExecutingLocked(r, execInFg, "create");
    boolean created = false;
    try {
        ....... //创建Service
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
    } catch (DeadObjectException e) {
      .......
    } finally {
        if (!created) {
            //服务创建done 拆除炸弹
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);
        }
    }
.......
}

Service创建前,肯定有很多准备工作,先判断要启动的service的进程是否存在,不存在还得走Zygote创建进程,然后进程创建后还得向AMS注册,如果进程存在,且ServiceRecord没有值,就表示之前没有创建过service,就走首次创建Service逻辑,Service创建前,就埋入bomb,Service创建完成,拆除bomb。

//from AMS
AMS----bumpServiceExecutingLocked(XXX)
    AMS----scheduleServiceTimeoutLocked(XXX)
    
static final int SERVICE_TIMEOUT = 20*1000;
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10; 
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
   .......
    Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    mAm.mHandler.sendMessageDelayed(msg,
       proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

所谓的埋入炸弹就是通过handler发了一个延时消息消息,延时的值前台服务20s、后台服务200s,这个Handler不是我们app的mH,是AMS进程中的Handler。

再看看真正创建Service的操作

//from ApplicationThread
public final void scheduleCreateService(IBinder token,
        ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
    updateProcessState(processState, false);
    CreateServiceData s = new CreateServiceData();
    s.token = token;
    s.info = info;
    s.compatInfo = compatInfo;
    sendMessage(H.CREATE_SERVICE, s);
}
public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
         .....
        case CREATE_SERVICE:
            handleCreateService((CreateServiceData)msg.obj);
            break;
          ......
//真正创建Service的逻辑
private void handleCreateService(CreateServiceData data) {
   LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
   Service service = null;
    try {//先创建上下文ContextImpl
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        //获取所在的进程
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        //直接反射创建Service实例
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
        //加载资源        
        context.getResources().addLoaders(
                app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
       .....
      //添加上下文
        service.attach(context, this, data.info.name, data.token, app,
                        ActivityManager.getService());
        service.onCreate();
        mServices.put(data.token, service);
        try { //binder调用AMS的serviceDoneExecuting
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (Exception e) {
        ......
    }
}

Service创建是在我们自己的app进程,自己通过反射构造的Service实例,调用service的onCreate的后,及时binder调用AMS的方法serviceDoneExecuting拆除(remove掉那个延时Message)。

//from AMS
AMS---serviceDoneExecuting(XXX)
   AMS---serviceDoneExecutingLocked(XXX)

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
        boolean finishing) {
    if (r.executeNesting <= 0) {
        if (r.app != null) {
            .......
            r.app.execServicesFg = false;
            r.app.executingServices.remove(r);
            if (r.app.executingServices.size() == 0) {
                //要启动的Service搞完了就移除消息
                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
            } else if (r.executeFg) {
              ......
            }
            ......
        }
       .....
    }
}

如果说Service创建时间很长,那就处理ActivityManagerService.SERVICE_TIMEOUT_MSG的超时Message。

//from AMS
final class MainHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        .....
        case SERVICE_TIMEOUT_MSG: {
            mServices.serviceTimeout((ProcessRecord)msg.obj);
    ........
void serviceTimeout(ProcessRecord proc) {
   ......
    if (anrMessage != null) {
        mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
    }
}

//from AppErrors
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
        ActivityRecord parent, boolean aboveSystem, final String annotation) {
   ......
        // Bring up the infamous App Not Responding dialog (infamous--臭名昭著的)
        Message msg = Message.obtain();
        msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
        msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
        mService.mUiHandler.sendMessage(msg);
    }
}

ANR的弹窗就可以显示出来了。(infamous--臭名昭著的)。
所以ANR跟Loopr.loop()完全是两个不同的功能,ANR需要Looper的支持,没得Looper,ANR不知道何时弹窗。

4.3、BlockCanary如何检测UI卡顿。

先看看Loopr.loop(),仔细看看在message处理前后有啥。

//from Looper
public static void loop() {
    ......
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // 可能阻塞
        .....
        //logger--   分发message
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        ......
        msg.target.dispatchMessage(msg);
       ...... 
        //logger--  message处理完成
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
        msg.recycleUnchecked(); //消息进入循环池
    }
}
public void setMessageLogging(@Nullable Printer printer) {
    mLogging = printer;
}

从上面看logging.println在msg处理前后都打印日志了,如果能够捕捉到日志打印的时机,基本是就可以知道handleMessage耗时多少了,拿到耗时就可以判断出是否卡顿了,而且贴心的是这个Printer是Looper的成员变量,可以让用户设置自己的Printer。

我们看看blockCanary的自定义Printer以及设置时机

class LooperMonitor implements Printer {
    private long mStartTimestamp = 0;
    private long mStartThreadTimestamp = 0;
   ......
    @Override
    public void println(String x) {
        if (mStopWhenDebugging && Debug.isDebuggerConnected()) {
            return;
        }
        if (!mPrintingStarted) {
            mStartTimestamp = System.currentTimeMillis();
            ......
        } else {
            final long endTime = System.currentTimeMillis();
            if (isBlock(endTime)) {
                notifyBlockEvent(endTime);
            }
           ......
        }
    }
private boolean isBlock(long endTime) {
    return endTime - mStartTimestamp > 3000;
}  

//设置Looper的Printer
//BlockCanary
public void start() {
    if (!mMonitorStarted) {
        mMonitorStarted = true;
        Looper.getMainLooper().setMessageLogging(new LooperMonitor(xxxx));
    }
}

BlockCanary自定义了Printer,然后捕捉两次时间diff,diff大于3s就认定有卡顿发生,可以dump出堆栈显示。设置Printer的时机是在入口类BlockCanary的start方法里面。

4.4、池化技术

池化技术采用享元设计模式实现,Message的池化、Glide中的BitmapPool、EventBus中的PendingPost等等。
我们关心的是池的设计,采用啥容器存储、size选取、以及recycle、obtain方法的处理。
Message的池化:存储采用单链表实现,链表长度是50,主要看obtain和recycle方法。

//Handler中Message的池化操作
public final class Message implements Parcelable {
    public static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;  //size最大50
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; 
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    public void recycle() {
        if (isInUse()) {
            return;
        }
        recycleUnchecked();
    }
    void recycleUnchecked() {
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
}

sPool、sPoolSync都是静态的,每个Message对象共享,obtain就是从单链表中取出一个Message,单链表没有就new 新的Message,recycle是先重置Message,然后看message池中size是否大于MAX_POOL_SIZE,超了就不加入,单链表的常规操作,简直不要太简单。

Glide中的BitmapPool

//LruBitmapPool默认: 只有1 or 4张的屏幕大小的Bitmap可以复用;
GroupedLinkedMap groupedMap = new GroupedLinkedMap<>();
存put 取get 基于有限的size来看是否需要trim。


EventBus采用ArrayList来存储,超了就不添加

private final static List pendingPostPool = new ArrayList();
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
    synchronized (pendingPostPool) {
        int size = pendingPostPool.size();
        if (size > 0) {
            PendingPost pendingPost = pendingPostPool.remove(size - 1);
            pendingPost.event = event;
            pendingPost.subscription = subscription;
            pendingPost.next = null;
            return pendingPost;
        }
    }
    return new PendingPost(event, subscription);
}
static void releasePendingPost(PendingPost pendingPost) {
    pendingPost.event = null;
    pendingPost.subscription = null;
    pendingPost.next = null;
    synchronized (pendingPostPool) {
        // Don't let the pool grow indefinitely
        if (pendingPostPool.size() < 10000) {
            pendingPostPool.add(pendingPost);
        }
    }
}


五、补充

MessageQueue 中还有一些native方法未涉及到,就是基于NativeMessagQueue做些其他的功能实现。是public的Api,目前暂未使用过。如有童鞋知道,可以评论告知。

private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
        @OnFileDescriptorEventListener.Events int events,
        @NonNull OnFileDescriptorEventListener listener) {
   .....
    synchronized (this) {
        updateOnFileDescriptorEventListenerLocked(fd, events, listener);
    }
}

六、相关推荐

【Android源码解析】Android中高级架构进阶学习——百大框架源码解析Retrofit/OkHttp/Glide/RxJava/EventBus...._哔哩哔哩_bilibili

本文转自 https://juejin.cn/post/7016291737039536142,如有侵权,请联系删除。

你可能感兴趣的:(Handler处理消息相关源码解析)