阅读 606

Android知识点之Handler

1、Handler的实现原理(基于Android 11)

(1)、消息循环队列的创建过程

当应用进程被启动时,调用ActivityThread.java的main()方法,进行消息循环队列创建 frameworks/base/core/java/android/app/ActivityThread.java

public static void main(String[] args) {
    ····
    //高版本废弃方法,由android环境创建
    Looper.prepareMainLooper();
    ·····
    Looper.loop();
    ····
}
复制代码

frameworks/base/core/java/android/os/Looper.java

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //sThreadLocal用于缓存Looper对象,线程局部变量,使变量线程独立性
    sThreadLocal.set(new Looper(quitAllowed));
}

public static void prepareMainLooper() {
    //创建一个Looper对象
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
复制代码

创建一个Looper对象时,会创建一个MessageQueue消息队列对象

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

一个MessageQueue消息队列对象创建时,会在C++层中创建一个NativeMessageQueue对象
frameworks/base/core/java/android/os/MessageQueue.java

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}
复制代码

调用nativeInit()方法进入C++层,返回一个long类型的NativeMessageQueue对象地址标识参数,C++层通过这个地址参数可获取到对应的NativeMessageQueue对象
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);
    //返回对象存放地址参数,long类型
    return reinterpret_cast<jlong>(nativeMessageQueue);
}
复制代码

在C++层中,NativeMessageQueue对象创建会创建一个Looper对象

NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    //获取当前线程创建的Looper对象
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}
复制代码

system/core/libutils/Looper.cpp

Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
mNextRequestSeq(0),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
    //主要是创建pipe管道的读端写端,mWakeEventFd是一个android::base::unique_fd对象
    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
    LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));

    AutoMutex _l(mLock);
    rebuildEpollLocked();
}

void Looper::rebuildEpollLocked() {
    ····
    //采用epoll机制进行监听读端写端,当一个线程没有新消息需要处理的时候,会休眠在管道读端文件描述符上,
    // 直到有其他线程消息通过写端把数据写入这个管道的读端,则该线程就会被唤醒
    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
    ····
}
复制代码

管道初始化读端写端,采用的内联函数实现
system/libbase/include/android-base/unique_fd.h

template <typename Closer>
    inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
                     int flags = O_CLOEXEC) {
        int pipefd[2];
#if defined(__linux__)
        if (pipe2(pipefd, flags) != 0) {
            return false;
        }
#else  // defined(__APPLE__)
        if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
            return false;
        }
        if (pipe(pipefd) != 0) {
            return false;
        }
        if (flags & O_CLOEXEC) {
            if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
                close(pipefd[0]);
                close(pipefd[1]);
                return false;
            }
        }
        if (flags & O_NONBLOCK) {
            if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
                close(pipefd[0]);
                close(pipefd[1]);
                return false;
            }
        }
#endif
        //管道读端,返回是对应id
        read->reset(pipefd[0]);
        //管道写端,返回是对应id
        write->reset(pipefd[1]);
        return true;
    }
复制代码

此时休眠与唤醒机制都已经建立完成

(2)、线程消息循环过程

当通过Looper.prepareMainLooper()完成消息循环队列创建后,调用Looper.loop()进入消息队列循环
frameworks/base/core/java/android/app/ActivityThread.java

public static void main(String[] args) {
    ····
    //高版本废弃方法,由android环境创建
    Looper.prepareMainLooper();
    ·····
    Looper.loop();
    ····
}
复制代码

frameworks/base/core/java/android/os/Looper.java

public static void loop() {
    //获取当前线程的Looper对象
    final Looper me = myLooper();
    ····
    //获取到当前线程消息队列对象
    final MessageQueue queue = me.mQueue;
    ····
    //通过一个无限循环读取消息
    for (;;) {
        //不断读取下一条消息,当没有消息时会在这个方法中休眠
        Message msg = queue.next(); // might block
        //消息为null则退出循环 (一般不会为null,除非主动调用退出Looper)
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ····
    }
}
复制代码

frameworks/base/core/java/android/os/MessageQueue.java

Message next() {
    // 判断C++层的NativeMessageQueue有没有创建,没有就退出
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    //记录空闲Handler的个数,当线程发现消息队列没有新消息需要处理时,不会立刻进入休眠状态
    //而是调用注册的IdleHandler在空闲时间执行一些操作
    int pendingIdleHandlerCount = -1;
    //表示没有新消息处理时,当前线程进入休眠等待状态的倒计时,
    //如果值为0,那么没有新消息也不会进入休眠
    //如果值为-1,表示当前线程需要进入休眠
    int nextPollTimeoutMillis = 0;
    //采用无限循环来执行消息队列
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            //释放有可能导致消息执行时被卡时间等对象
            Binder.flushPendingCommands();
        }
        //C++层的休眠与唤醒操作,通过管道的读端监听是否有新消息处理,并且把消息赋值到下文的‘mMessages’,至此激活消息循环操作
        //nextPollTimeoutMillis如果值为0,那么没有新消息也不会进入休眠
        //nextPollTimeoutMillis如果值为-1,表示当前线程需要进入休眠
        //nextPollTimeoutMillis如果值大于0,表示当前线程需要定时nextPollTimeoutMillis的时间,然后再执行下面程序
        nativePollOnce(ptr, nextPollTimeoutMillis);
        //同步处理
        synchronized (this) {
            //获取当前循环时间
            final long now = SystemClock.uptimeMillis();
            //上一条消息定义
            Message prevMsg = null;
            //当前需要处理的消息
            Message msg = mMessages;
            //msg.target=null时代表当前消息是一个同步屏障消息
            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 {
                    //立即执行当前消息
                    mBlocked = false;
                    //设置了同步屏障器,那么prevMsg不为null
                    if (prevMsg != null) {
                        //将当前的异步消息下一条消息赋值到上一条的下一条
                        //这样保持原有的同步消息队列
                        //下一次循环还是执行mMessages消息
                        //此处可以看到,当有同步屏障器时异步消息不会受到阻塞,
                        //并且此时的消息队列有一定的排序,也就是异步消息也是跟同步消息一样根据设置时间大小来排序到消息队列中
                        prevMsg.next = msg.next;
                    } else {
                        //否则将当前的下一条消息赋值mMessages
                        mMessages = msg.next;
                    }
                    //将下一个置为null
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    //标记使用中
                    msg.markInUse();
                    //返回当前消息
                    return msg;
                }
            } else {
                //没有消息,表示线程即将进入休眠
                nextPollTimeoutMillis = -1;
            }
            //主线程的mQuitting不会为true,具体可以看到quit()方法
            //如果在主线程调用quit()方法,将会抛出"Main thread not allowed to quit."异常
            //因此,主线程是不会退出无限循环的
            if (mQuitting) {
                dispose();
                return null;
            }
            //当IdleHandler数量小于0且当前时间没有消息需要执行时,读取到空闲Handler的数量来执行IdleHandler
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                //没有要运行的空闲处理程序。循环再等一会儿。
                mBlocked = true;
                //进入下一轮循环
                continue;
            }
            //pendingIdleHandlerCount有值时,创建一个IdleHandler池,最小为4个数量级
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new MessageQueue.IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            //将注册的IdleHandler空闲消息填充到mPendingIdleHandlers池中,等待下一步执行
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        //执行空闲的Handler消息, 全部执行完
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            //获取到需要执行的空闲handler
            final MessageQueue.IdleHandler idler = mPendingIdleHandlers[i];
            //释放对处理程序的引用
            mPendingIdleHandlers[i] = null;
            //用于判断是否需要将IdleHandler在mIdleHandlers列表中移除
            //false 为移除; true 为保留
            boolean keep = false;
            try {
                //获取注册IdleHandler是否保存消息的返回值
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                //从mIdleHandlers列表中移除IdleHandler
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        //将空闲处理程序计数重置为 0,防止多次运行到空闲Handler
        pendingIdleHandlerCount = 0;
        //在调用空闲处理程序时,可能已经传递了一条新消息,因此无需等待即可返回并再次查找待处理的消息。
        nextPollTimeoutMillis = 0;
    }
}
复制代码

此处消息能无误顺利执行,是因为添加消息时都按照时间从小到大顺序排序过,是发送消息时进行排序的,具体看到(3)线程消息发送过程,这里包含同步消息与异步消息,但是官方在Message.setAsynchronous()说“请注意,异步消息可能会乱序传递”,在代码里没有发现有这个体现,让人疑惑???

休眠与唤醒操作是在C++层实现
frameworks/base/core/jni/android_os_MessageQueue.cpp

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
                                                   jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    //在Looper处理
    mLooper->pollOnce(timeoutMillis);
    ·····
}
复制代码

system/core/libutils/include/utils/Looper.h

int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
inline int pollOnce(int timeoutMillis) {
    return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
}
复制代码

system/core/libutils/Looper.cpp

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    //通过无限循环去读取结果
    for (;;) {
        ····
        if (result != 0) {
            ····
            return result;
        }
        result = pollInner(timeoutMillis);
    }
}

int Looper::pollInner(int timeoutMillis) {
    ····
    // Poll.
    int result = POLL_WAKE;
    ·····
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    //进入休眠等待状态,等待时间由timeoutMillis参数决定
    //timeoutMillis如果值为0,那么没有新消息也不会进入休眠
    //timeoutMillis如果值为-1,表示当前线程需要进入休眠
    //timeoutMillis如果值大于0,表示当前线程需要定时timeoutMillis的时间,然后再执行下面程序
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ····
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd.get()) {
            if (epollEvents & EPOLLIN) {
                //如果检测到当前线程的IO读写事件发送变化,需要唤醒休眠线程
                awoken();
            }
            ···
        }
        ····
    }
    ····
    return result;
}

void Looper::awoken() {
    ····
    uint64_t counter;
    //简单读取进入下一循环
    TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}
复制代码
(3)、线程消息发送过程

线程进入休眠是怎么被唤醒的,需要了解线程消息的发送过程,以ActivityThread.java中的H对象为例,H对象创建
frameworks/base/core/java/android/os/Handler.java

public Handler(@Nullable Callback callback, boolean async) {
    ····
    //获取到当前线程的Looper,myLooper()是通过sThreadLocal获取Looper,而ThreadLocal是以线程中ThreadLocalMap来存储Looper,ThreadLocalMap是线程独立持有的,由ThreadLocal对象间接操作
    mLooper = Looper.myLooper();
    //如果子线程没有创建Looper,此处会为null抛出异常
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    ····
}
复制代码

使用H对象发送信息
frameworks/base/core/java/android/app/ActivityThread.java

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    ····
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}
复制代码

frameworks/base/core/java/android/os/Handler.java

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

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

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    ····
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                               long uptimeMillis) {
    //将当前的Handler封装到Message对象中
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

frameworks/base/core/java/android/os/MessageQueue.java

boolean enqueueMessage(Message msg, long when) {
    //msg.target就是Handler对象
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    //同步执行
    synchronized (this) {
        //判断是否被使用,当执行消息时,在next()中也会对这个参数进行标记使用过
        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) {
            //当前的msg消息执行的优先级大于mMessages,替换过来
            //mMessages这个值也是唤醒消息队列的依据之一,当不为null时唤醒消息队列
            //查看next()方法便可以知晓
            msg.next = p;
            mMessages = msg;
            //判断是否需要唤醒,mBlocked在next()方法中判断赋值,当空闲Handler执行完,mBlocked=true
            needWake = mBlocked;
        } else {
            //当前的msg消息执行的优先级小于mMessages
            // 插入队列中间。通常我们不必唤醒事件队列,除非队列的头部有一个屏障消息,并且消息是队列中最早的异步消息。
            //判断当前的msg消息是否需要唤醒
            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;
        }
        //如果当前的消息是立即执行,也就是需要唤醒,那么通过epoll机制进行唤醒
        //这时在next()方法中的nativePollOnce(ptr, nextPollTimeoutMillis)方法就不会继续阻塞休眠,让消息循环得以继续执行
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}
复制代码

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

void NativeMessageQueue::wake() {
    mLooper->wake();
}
复制代码

system/core/libutils/Looper.cpp

void Looper::wake() {
    ···
    uint64_t inc = 1;
    //此时向管道写入一条数据,这个时候的目标线程就会因为这个管道发生一个IO写事件被管道读端监测到而被唤醒
    //这个被监测是在'(2)、线程消息循环过程'中的'int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis)'监测到IO读写事件变化
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
    ····
}
复制代码

到此,发送一条消息并且唤醒了当前线程的消息队列,在MessageQueue.java的next()方法中就会返回当前需要执行的消息,然后到了消息处理阶段

(4)、消息处理过程

在MessageQueue.java的next()方法中就会返回当前需要执行的消息 frameworks/base/core/java/android/os/Looper.java

public static void loop() {
    final Looper me = myLooper();
    ···
    final MessageQueue queue = me.mQueue;
    ····
    for (;;) {
        //返回当前需要处理的消息
        Message msg = queue.next(); // might block
        ····
        try {
            //msg.target对应的就是注册的Handler对象
            msg.target.dispatchMessage(msg);
            ····
        } catch (Exception exception) {
            ····
        } 
        ·····
    }
}
复制代码

frameworks/base/core/java/android/os/Handler.java

public void dispatchMessage(@NonNull Message msg) {
    //是否Message自定义回调接口
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //是否Handler指定了一个回调接口
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //没有回调接口执行到此
        handleMessage(msg);
    }
}

//如果是通过post发送消息或者自定义Runnable回调接口的则调用此方法
private static void handleCallback(Message message) {
    message.callback.run();
}

//非自定义回调Runnable接口的回调到此处
public void handleMessage(@NonNull Message msg) {
    //此方法是注册Handler对象重写的方法
}
复制代码

至此,消息循环发送整个流程完成,这个消息需要先加入消息队列再进一步处理,而空闲消息是不需要加入消息队列的

(5)、IdleHandler消息发送流程

将IdleHandler消息加入消息队列的mIdleHandlers中 frameworks/base/core/java/android/os/MessageQueue.java

public void addIdleHandler(@NonNull IdleHandler handler) {
    ····
    synchronized (this) {
        mIdleHandlers.add(handler);
    }
}
复制代码

将IdleHandler消息加入消息队列的mIdleHandlers中后,会通过next()方法利用消息队列空闲时间去执行,具体看到(2)、线程消息循环过程的next()方法描述

(6)、异步消息发送流程

异步消息与同步消息也是加入到消息队列里执行,只不过异步消息不会受到同步屏障器的影响 frameworks/base/core/java/android/os/MessageQueue.java

boolean enqueueMessage(Message msg, long when) {
    ····
    //mBlocked:当没有消息执行的时候且没有空闲Handler执行时为true
    //p.target == null:当p是同步屏障消息则为true
    //msg.isAsynchronous():当消息设置为异步时为true
    //以上条件满足即是唤醒消息队列,也就是说异步消息与同步消息也是遵循'先进先出'规则
    //同时异步消息也受休眠控制
    //但是异步消息不会受到同步屏障的阻塞,具体可以看到next()方法中的实现
    needWake = mBlocked && p.target == null && msg.isAsynchronous();
    ····
}
复制代码
(7)、屏障消息发送流程

设置一个屏障消息,屏障消息之前的消息可以正常运行,屏障消息之后的同步消息将会被阻塞,异步消息不会受到同步屏障的影响,可以正常运行 (此方法为系统调用方法,不开放)
frameworks/base/core/java/android/os/MessageQueue.java

public int postSyncBarrier() {
    return postSyncBarrier(SystemClock.uptimeMillis());
}

private int postSyncBarrier(long when) {
    //创建一个屏障消息加入消息队列
    synchronized (this) {
        final int token = mNextBarrierToken++;
        //创建一条消息,msg.target是不会去设置,因此msg.target=null代表的是同步屏障消息
        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;
    }
}
复制代码

将同步屏障消息排入消息队列完成,同步屏障消息的执行是在next()方法中,具体可以查看(2)、线程消息循环过程 的next()执行流程

设置同步屏障消息如果不移除,后续的同步消息将会一直挂起,也就是阻塞中,因此使用同步屏障消息到达目的后需要移除同步屏障消息
frameworks/base/core/java/android/os/MessageQueue.java

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;
        }
        //判空操作,此时的p代表的是同步屏障消息
        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) {
            //移除消息队列里p同步屏障消息
            prev.next = p.next;
            //由于同步消息在消息队列里面(非首消息),不需要去唤醒消息循环
            needWake = false;
        } else {
            //移除首消息的p同步屏障消息
            mMessages = p.next;
            //首消息不存在或者首消息非同步屏障消息时,唤醒消息循环
            needWake = mMessages == null || mMessages.target != null;
        }
        //回收消息
        p.recycleUnchecked();
        //在C++层通过epoll管道监听IO读写机制唤醒消息队列
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}
复制代码

至此,消息处理机制分析完成

2、子线程不能直接new一个handler,为什么主线程可以?

创建Handler对象时,会去获取当前线程的Looper对象,Looper对象是缓存在ThreadLocal对象中,ThreadLocal对象是线程变量缓存类,每个线程中都维护着ThreadLocalMap,由ThreadLocal对象间接操作,通过以ThreadLocal对象为key来缓存线程数据,使得线程之间数据相互独立
当在子线程创建一个Handler对象,由于子线程没有创建Looper对象,因此会报运行异常的错误 Handler源码:

public Handler(@Nullable Callback callback, boolean async) {
    ····
    //获取到当前线程的Looper,myLooper()是通过sThreadLocal获取Looper,而ThreadLocal是以线程中ThreadLocalMap来存储Looper,ThreadLocalMap是线程独立持有的,由ThreadLocal对象间接操作
    mLooper = Looper.myLooper();
    //如果子线程没有创建Looper,此处会为null抛出异常
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    ····
}
复制代码

Looper源码:

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

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
复制代码

主线程可以创建Handler对象是因为当应用进程启动起来时,系统调用到ActivityThread对象的main方法,在main方法中通过Looper.prepareMainLooper()创建了一个Looper对象并且缓存在ThreadLocal对象中,因ThreadLocal对象中缓存了主线程的Looper对象,所以主线程可以创建Handler对象

3、主线程的Looper第一次调用loop方法什么时候?哪个类?

当应用进程被卵化器进程启动时,会调用java层ActivityThread.java对象的main方法,在main方法中通过Looper.prepareMainLooper()创建了一个Looper对象并且缓存在ThreadLocal对象中 ActivityThread源码:

public static void main(String[] args) {
    ····
    //高版本废弃方法,由android环境创建
    Looper.prepareMainLooper();
    ·····
    Looper.loop();
    ····
}
复制代码

4、Handler导致的内存泄漏原因及其解决方案

原因:
(1)非静态的内部类和匿名内部类都会隐式的持有一个外部类的引用,静态内部类则不会持有外部类的引用
(2)在Activity销毁时,由于Handler可能有未执行完消息或者正在执行的消息,导致Handler持有Activity的引用,进而导致GC无法回收Activity。

解决方案:
(1)使用静态或者弱应用来创建Handler
(2)在Activity销毁时,需要先回收掉Handler发送的消息

5、一个线程可以有几个Handler?几个Looper?几个MessageQueue对象?

在内存允许的情况下,一个线程可以有无数个Handler,一个线程只能拥有一个Looper对象以及一个MessageQueue对象。
Looper源码:

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        //线程只能创建一个Looper,否则报错
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //sThreadLocal用于缓存Looper对象,用于缓存线程局部变量,使变量线程独立性
    sThreadLocal.set(new Looper(quitAllowed));
}

public static void prepareMainLooper() {
    //创建一个Looper对象
    prepare(false);
    synchronized (Looper.class) {
        //主线程只能创建一个sMainLooper,否则报错
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
复制代码

由于MessageQueue对象是在Looper对象构造方式中创建的,因此一个线程也只能创建一次
Looper源码:

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

6、Message对象的创建方式有哪些?有什么区别?

创建方式:
(1)通过Handler对象的obtainMessage方法创建(实际上通过调用Message.obtain方法创建)
(2)通过调用Message.obtain方法创建
(3)通过new Message()来创建
区别:
Message.obtain方法内部维护着一个常量池,通过利用已经使用完成的Message对象来重复使用,避免频繁创建Message;通过new Message()来创建则没有常量池维护,避免不了频繁创建Message
Message源码:

private static final int MAX_POOL_SIZE = 50;
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            //重复利用Message
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

void recycleUnchecked() {
    ····
    //当消息被执行完进行回收时,如果没有达到最大常量池50的数量级则会被重复利用
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}
复制代码

7、Message.obtain()怎么维护消息池的?

Message源码:

private static final int MAX_POOL_SIZE = 50;
public static Message obtain() {
    synchronized (sPoolSync) {
        //sPool为null的时候即是sPoolSize超出MAX_POOL_SIZE数目
        if (sPool != null) {
            //重复利用Message
            Message m = sPool;
            //被回收的next值为null
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    //超出MAX_POOL_SIZE数量级或者sPool=null则重新创建
    return new Message();
}

void recycleUnchecked() {
    ····
    //当消息被执行完进行回收时,如果没有达到最大常量池50的数量级则会被重复利用
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            //当消息发送处理完成时,next存储的消息对象会在MessageQueue的next()方法中置为null,同时会在Looper的loop()方法中调用当前recycleUnchecked()进行回收消息对象
            //进入此逻辑sPool一般为null,可以推算得出
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}
复制代码

Message中有个静态消息池变量,当消息池统计数量不超过50数量级时,则缓存被回收的Message对象以便在创建新消息时重复利用,当消息池统计数量超过50数量级时,消息池将不再缓存回收的Message对象

8、Handler有哪些发送消息的方法

post :将 Runnable r 添加到消息队列中。 runnable 将在此处理程序附加到的线程上运行。

postAtTime :使 Runnable r 添加到消息队列中,在 uptimeMillis 给定的特定时间运行。

postDelayed :使 Runnable r 添加到消息队列中,在指定的时间后运行。 runnable 将在此处理程序附加到的线程上运行。

postAtFrontOfQueue : 将消息发布到实现 Runnable 的对象。使 Runnable r 在下一次迭代时通过消息队列执行。 runnable 将在此处理程序附加到的线程上运行。

runWithScissors : 同步运行指定的任务。在当前任务执行完后线程继续执行

sendMessage :在当前时间之前的所有待处理消息之后,将消息推送到消息队列的末尾。它将在 {@link handleMessage} 中接收,在附加到此处理程序的线程中。

sendEmptyMessage : 发送仅包含 what 值的消息。

sendEmptyMessageDelayed : 发送一条只包含 what 值的消息,在指定的时间段过去后传送。

sendEmptyMessageAtTime : 发送只包含 what 值的消息,在特定时间传递。

sendMessageDelayed : 在所有待处理消息之后(当前时间 + delayMillis)将消息排入消息队列。您将在 {@link handleMessage} 中收到它,在附加到此处理程序的线程中。

sendMessageAtTime : 在绝对时间(以毫秒为单位uptimeMillis之前的所有待处理消息之后,将消息排入消息队列。 时基为 {@link android.os.SystemClockuptimeMillis}。深度睡眠时间会增加执行的额外延迟。您将在 {@link handleMessage} 中收到它,在附加到此处理程序的线程中。

sendMessageAtFrontOfQueue : 将消息排在消息队列的前面,以在消息循环的下一次迭代中进行处理。您将在 {@link handleMessage} 中收到它,在附加到此处理程序的线程中。这个方法只在非常特殊的情况下使用——它很容易导致消息队列阻塞,导致排序问题,或者有其他意想不到的副作用。

executeOrSendMessage : 如果在此处理程序对应的同一线程上调用,则同步执行消息,否则 {@link sendMessage 将其推送到队列}
复制代码

9、Handler的post与sendMessage的区别与应用场景?

通过post发送需要定义一个Runnable接口作为消息执行完成的回调,回调不携带消息内容,并且Message内容是无法自定义的,通常用于定时处理任务以及线程切换
sendMessage可以自定义Message内容,如果有设置Runnable接口,那么消息执行完会回调到run方法,如果未自定义设置Runnable接口,则通过handleMessage()方法回调消息,通常用于数据传输(包括线程间数据传输)

10、MessageQueue是什么数据结构

消息队列是单向链表结构,在Message对象中有一个变量next,next变量是被定义为持有下一个Meassage对象

11、Handler怎么做到的一个线程只有对应一个Looper,如何保证只有一个MessageQueue

Looper对象是通过ThreadLocal进行缓存的,ThreadLocal是线程局部变量,内部维护一个独立ThreadLocalMap,通过以ThreadLocal对象为key缓存Looper对象,所以在线程中通过ThreadLocal获取的Looper就是线程创建的Looper
Handler源码:

public Handler(@Nullable Callback callback, boolean async) {
    ····
    //获取到当前线程的Looper,myLooper()是通过sThreadLocal获取Looper,而ThreadLocal是以线程中ThreadLocalMap来存储Looper,ThreadLocalMap是线程独立持有的,由ThreadLocal对象间接操作
    mLooper = Looper.myLooper();
    //如果子线程没有创建Looper,此处会为null抛出异常
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    ····
}
复制代码

Looper源码:

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

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //通过ThreadLocal缓存Looper对象
    sThreadLocal.set(new Looper(quitAllowed));
}    

public static @Nullable Looper myLooper() {
    //通过ThreadLocal获取Looper对象
    return sThreadLocal.get();
}
复制代码

ThreadLocal源码:

public T get() {
    //获取到当前线程
    Thread t = Thread.currentThread();
    //通过当前线程获取ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //以ThreadLocal对象为key读取到缓存值返回
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //当map为null是就返回定义的初始化值
    return setInitialValue();
}

public void set(T value) {
    //获取当前线程
    Thread t = Thread.currentThread();
    //通过当前线程获取ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    if (map != null)
        //当有ThreadLocalMap对象时,将参数以ThreadLocal对象为key缓存到线程的ThreadLocalMap的数组中
        map.set(this, value);
    else
        //当没有ThreadLocalMap对象时先创建,将参数缓存到线程的ThreadLocalMap的数组中
        createMap(t, value);
}

//通过当前线程获取ThreadLocalMap对象
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

//为当前线程创建一个独立的ThreadLocalMap对象,并且把参数缓存进去
void createMap(Thread t, T firstValue) {
    //将参数以ThreadLocal对象为key缓存到线程的ThreadLocalMap的数组中
    //赋值给线程独立的变量threadLocals
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
复制代码

Thread源码:

//线程中接收ThreadLocalMap对象变量
ThreadLocal.ThreadLocalMap threadLocals = null;
复制代码

因此当线程Looper已经创建,ThreadLocal.ThreadLocalMap就会有缓存,因此可以通过ThreadLocal对象获取到Looper对象,所以再创建一个Looper就会报错:
Looper源码:

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //通过ThreadLocal缓存Looper对象
    sThreadLocal.set(new Looper(quitAllowed));
}
复制代码

至此,Handler创建保证了只有一个Looper,由于MessageQueue是Looper构造函数创建的,因此也保证了只有一个MessageQueue
Looper源码:

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

12、ThreadLocal在Handler机制中的作用

ThreadLocal在Handler机制中是用于缓存Looper对象,确保线程间的Looper独立性与唯一性

13、HandlerThread是什么?有什么好处?实现原理?使用场景?

HandlerThread继承了Thread,在run方法中维护着Looper消息循环机制,是一个子线程消息循环机制实现对象,实现的原理与主线程消息循环机制一样。
HandlerThread的消息循环机制可以进行耗时操作,同时具备通过消息队列有序的控制消息执行
原理:

public class HandlerThread extends Thread {
    ····
    @Override
    public void run() {
        mTid = Process.myTid();
        //子线程中维护着looper循环机制
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    ····
}

Looper循环机制原理与主线程Looper消息循环机制原理一致
复制代码

使用场景:
(1)耗时操作以及需要有序的执行耗时操作
(2)执行多个独立的耗时操作任务

14、IdleHandler及其使用场景

IdleHandler是空闲Handler消息,IdleHandler发送的消息是不会加入消息队列(指的是消息排序链表数据)中,通过加入mIdleHandlers列表中,在消息队列空闲时执行
使用场景-->ActivityThread.java源码:

//GC操作
final class GcIdler implements MessageQueue.IdleHandler {
    @Override
    public final boolean queueIdle() {
        doGcIfNeeded();
        purgePendingResources();
        return false;
    }
}

//清除资源操作
final class PurgeIdler implements MessageQueue.IdleHandler {
    @Override
    public boolean queueIdle() {
        purgePendingResources();
        return false;
    }
}
复制代码

当queueIdle()方法返回true,idleHandler执行完不会从mIdleHandlers列表中移除,保留下来等待下一个空闲消息后执行,如果返回false,就会从mIdleHandlers列表中移除

15、消息屏障,同步屏障机制

屏障消息,就是一条Message中target为null(即不携带Handler对象)或者具有特定token的Message
当同步屏障消息加入消息队列中,是按照设置的时间从小到大排序在队列中,遵循'先进先出'规则(可以参考第1问题的(7)屏障消息发送流程源码),排序在屏障消息之后的同步消息将会被阻塞或者挂起,之后的同步消息将不会执行,只有撤销屏障消息后才能执行之后的同步消息(可以参考第1问题的(2)线程消息循环过程源码的next()方法),异步消息不会受同步屏障消息的影响

16、子线程能不能更新UI

在Activity的onResume()生命周期之前,是可以子线程更新UI的,在这个生命周期方法之后子线程更新UI会报错:
frameworks/base/core/java/android/view/ViewRootImpl.java

//UI检测当前线程
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
复制代码

溯源checkThread()的调用时机,可以追踪处于什么生命周期时机里,既然是与View有关,那么可以追踪View添加与显示的时机,而onResume()生命周期方法是View显示出来的时机,那么追踪到ActivtyThread的handleResumeActivity(),这个是调用onResume()生命周期方法的入口
frameworks/base/core/java/android/app/ActivityThread.java

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
                                 String reason) {
    ···
    //此处是调用到onResume()生命周期方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    ····
    //Activity调用attach()方法创建出PhoneWindow对象,attach()方法在onResume()生命周期方法之前,因此进入这个判断
    if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
        ····
        //View的添加与显示
        if (r.activity.mVisibleFromClient) {
            r.activity.makeVisible();
        }
    }
}
复制代码

frameworks/base/core/java/android/app/Activity.java

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        //添加View,wm对应的是WindowManagerImpl对象
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}
复制代码

frameworks/base/core/java/android/view/WindowManagerImpl.java

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    //mGlobal对应的是WindowManagerGlobal对象
    mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
            mContext.getUserId());
}
复制代码

frameworks/base/core/java/android/view/WindowManagerGlobal.java

public void addView(View view, ViewGroup.LayoutParams params,
                    Display display, Window parentWindow, int userId) {
    ····
    ViewRootImpl root;
    ···
    //检测线程是在ViewRootImpl对象中,ViewRootImpl对象在onResume()生命周期方法才创建
    root = new ViewRootImpl(view.getContext(), display);
    ····
    //将View设置到ViewRootImpl
    root.setView(view, wparams, panelParentView, userId);
    ···
}
复制代码

frameworks/base/core/java/android/view/ViewRootImpl.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
                    int userId) {
    ····
    //在添加到窗口管理器之前安排第一个布局,
    //以确保我们在从系统接收任何其他事件之前进行重新布局。
    //此处是View最开始的布局,在这里也会检验线程
    requestLayout();
    ····
}

public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        //检验当前线程
        checkThread();
        ····
    }
}

void checkThread() {
    //如果当前线程不是主线程,则抛出异常
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}
复制代码

通过异常报错追踪出时机点,是在onResume()生命周期方法创建ViewRootImpl对象加入线程检测,因此在onResume()生命周期方法之前是不会报错,也就是可以子线程更新UI

17、为什么Android系统不建议子线程访问UI

因为UI控件不是线程安全的,如果多线程并发访问UI控件可能会出现不可预期的状态
那为啥不加锁或者同步处理呢?

  • 一是同步或者加锁会让UI访问变得复杂;
  • 二是同步或者加锁加锁会降低UI访问效率,会阻塞一些线程访问UI

18、Android中为什么主线程不会因为Looper.loop()里的死循环卡死

一个应用运行全过程都处于这个主线程的Looper.loop()循环里,主线程Looper.loop()循环退出意味着退出应用,Looper.loop()循环维护着消息队列,在没有消息的时候会通过Linux管道(Pipe)和epoll机制进入休眠,Linux管道(Pipe)和epoll机制监听IO读写操作,当有消息时会加入消息队列触发唤醒(是向当前线程管道读端写入一个文件描述符,IO读写操作发生变化被监听到触发唤醒)继续执行Looper.loop()循环处理消息队列,这个消息可以是界面点击、滑动、输入等等

19、MessageQueue#next 在没有消息的时候会阻塞,如何恢复?

消息队列在没有消息时,通过Linux管道(Pipe)和epoll机制将线程休眠,当有即时消息加入时,通过向pipe管道读端写入一个文件描述符,此时通过epoll机制监测到当前线程的读端有IO操作,进而唤醒当前线程继续执行消息队列

20、Handler消息机制中,一个looper是如何区分多个Handler的

消息队列的消息是通过Message对象进行封装,当通过Handler发送一条消息是会在Message对象的target变量赋值当前的Handler对象,也就是Message消息携带了Handler对象,处理到当前消息时再从Message中取出Handler对象,相当于透传,以这种方式区分(同步屏障消息除外)

21、当Activity有多个Handler的时候,怎么样区分当前消息由哪个Handler处理

参考20

22、处理message的时候怎么知道是去哪个callback处理的

(1)消息队列的消息是通过Message对象进行封装,Message对象中有一个Runnable对象类型的callback参数,当消息队列处理到此消息时,会判断Message对象中的callback参数是否为null,不为null就通过callback参数回调
(2)根据20题可知Message对象携带了Handler对象,当创建Handler对象定义了Handler.Callback接口时,此时Handler对象mCallback初始化了一个回调接口,当消息队列处理到此消息时,会判断Handler对象中的mCallback参数是否为null,不为null就通过callback参数回调
(1)优先级大于(2),Handler源码:

public void dispatchMessage(@NonNull Message msg) {
    //判断Message中的callback
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //判断handler中的mCallback
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //最后才是Handler方法接管消息
        handleMessage(msg);
    }
}
复制代码

23、Looper.quit/quitSafely的区别

quit : 移除所有在消息队列的消息(包括正在执行消息)并且不允许消息加入。会导致Looper.loop()方法终止而不处理消息队列中的任何消息。在Looper被要求退出后,任何向队列发布消息的尝试都将失败。使用此方法可能不安全,因为在循环程序终止之前,某些消息可能无法传递。

quitSafely :移除消息队列设置的时间未到期的消息,当前到期的消息可以继续执行完。使Looper.loop()方法在处理完消息队列中已经到期的所有剩余消息后立即终止。但是,在循环终止之前,不会传递具有未来到期时间的待处理延迟消息,也就是说终止前可以加入即时消息。

24、通过Handler如何实现线程的切换

1题源码分析知道,Looper循环执行的消息队列是具体在某一个线程运行的,也就是通过Looper处理的消息最终会在Looper所在的线程返回,而Handler发送消息不仅可以在同线程发送,也可以在异线程发送,最终消息的回调取决于Handler创建时所绑定的Looper,因此Handler通过Looper循环的消息队列实现线程之间的切换

25、Handler 如何与 Looper 关联的

Handler在创建时可以传一个特定的Looper,当不传特定的Looper时,Handler在创建时会通过Looper.myLooper()会去获取当前创建Handler线程的Looper,如果当前线程有创建Looper对象,会缓存在ThreadLocal中,ThreadLocal是线程局部变量,内部维护一个独立ThreadLocalMap,通过以ThreadLocal对象为key缓存Looper对象,而ThreadLocalMap对象是缓存在具体线程Thread对象threadLocals变量中,所以在线程中通过ThreadLocal获取的Looper就是线程创建的Looper, 源码分析请看到11

26、Looper 如何与 Thread 关联的

每个Looper对象会缓存在ThreadLocal中,ThreadLocal是线程局部变量,内部维护一个独立ThreadLocalMap,通过以ThreadLocal对象为key缓存Looper对象,而ThreadLocalMap对象是缓存在具体线程Thread对象threadLocals变量中,所以通过ThreadLocal对象获取Looper对象,实际上是获取Thread对象threadLocals变量的ThreadLocalMap对象,通过以ThreadLocal对象为key拿到Looper对象,因此在线程中通过ThreadLocal获取的Looper就是线程创建的Looper,Looper与Thread关联是通过ThreadLocal建立的

27、Looper.loop()源码

参考1题的第 (2)、线程消息循环过程 节以及第(4)、消息处理过程

28、MessageQueue的enqueueMessage()方法如何进行线程同步的

消息队列是单向链表数据结构,Message中有一个参数next是缓存下一个Message对象的变量,每个Message都设置有一个执行时间,enqueueMessage()方法通过设置的时间按照从小到大依次排序,消息循环通过消息队列循序依次取出排序的消息进行处理,以此方式进行线程同步执行,源码参考1题的第(4)、消息处理过程

29、MessageQueue的next()方法内部原理

主要分为四步:

  • 调用nativePollOnce方法进入C++层处理休眠唤醒
  • 处理同步屏障消息
  • 处理消息队列返回消息
  • 处理空闲Handler的执行

源码参考参考1题的第 (2)、线程消息循环过程

30、子线程中是否可以用MainLooper去创建Handler,Looper和Handler是否一定处于一个线程

MainLooper如果已经创建了,那么就不可以再次创建,否则可以创建。但是在android应用中,应用的启动是伴随MainLooper的创建,子线程如果再次去创建MainLooper将会报错:

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        //应用启动是在ActivityThread中main方法已经创建了sMainLooper,因此不可以再差创建
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
复制代码

Handler的创建可以指定Looper对象,在子线程使用MainLooper去创建Handler是允许的,相当于是线程切换操作:

public Handler(@NonNull Looper looper) {
    this(looper, null, false);
}
复制代码

Looper的创建与循环需要在同一个线程,否则会报错:

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        //说明当前线程没有创建Looper
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    if (me.mInLoop) {
        Slog.w(TAG, "Loop again would have the queued messages be executed"
                + " before this one completed.");
    }
    ····
}
复制代码

Handler是可以自由指定Looper来创建的,因此操作不会限定在同一个线程,可以同异线程发送消息等等,但是回调的消息是在Looper所在的线程中
Looper和Handler可以不处于同一个线程中,线程切换就是异线程操作

31、ANR和Handler的联系

ANR 即 Application Not Response, 是系统进程对应用行为的一种监控,如果应用程序没有在规定时间内完成任务的话,就会引起ANR,在规定时间内没有执行完操作引起ANR的情况:

  • Service Timeout: 前台服务20s, 后台服务200s
  • BroadcastQueue Timeout: 前台广播 10s,后台广播60s
  • ContentPrivider Timeout: 10s
  • InputDispatching Timeout: 5s

ANR弹窗的操作过程是通过Handler机制建立的,ANR弹窗是系统弹窗,是在SystemServer进程中
以Server的启动为例,Server启动前通过Handler机制在消息队列加入一个超时定时消息任务,当Server启动完成时则移除这条消息任务,当Server启动超过这个设定的时间时则会弹出一个系统ANR提示窗口

(1)、启动Server超时任务

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

private final void realStartServiceLocked(ServiceRecord r,
                                          ProcessRecord app, boolean execInFg) throws RemoteException {
    ····
    //启动超时定时ANR任务
    bumpServiceExecutingLocked(r, execInFg, "create");
    //接下来就是启动Server一系列操作,或者计算在Server中的onCreate()生命周期方法是否有耗时操作
    ····
    try {
        ····
        //进入应用进程启动,移除超时定时ANR任务操作也在此处
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                app.getReportedProcState());
        ····
        //完成启动
        created = true;
    } catch (DeadObjectException e) {
        ····
    } 
    ···
}

private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
    ····
    //开启超时任务
    scheduleServiceTimeoutLocked(r.app);
    ···
}

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    ····
    //通过Handler发送一个定时任务,当时间到没有移除这个任务,则认为是超时
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    mAm.mHandler.sendMessageDelayed(msg,
            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
复制代码

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

final class MainHandler extends Handler {
    ····
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            ····
            //当Server超时的时候,回调到此处,准备弹ANR窗口
            case SERVICE_TIMEOUT_MSG: {
                mServices.serviceTimeout((ProcessRecord)msg.obj);
            } break;
            ···
            //当内容提供者超时,回调到此处,准备弹ANR窗口
            case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
                ProcessRecord app = (ProcessRecord)msg.obj;
                synchronized (ActivityManagerService.this) {
                    processContentProviderPublishTimedOutLocked(app);
                }
            } break;
            ····
        }
    }
}
复制代码

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

//前台server超时时间
static final int SERVICE_TIMEOUT = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
//后台server超时时间
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

void serviceTimeout(ProcessRecord proc) {
    String anrMessage = null;
    synchronized(mAm) {
       ····
        if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) {
            ····
            //超时后来到此处anrMessage赋值
            anrMessage = "executing service " + timeout.shortInstanceName;
        } else {
            //初始化定时,设定超时时间
            Message msg = mAm.mHandler.obtainMessage(
                    ActivityManagerService.SERVICE_TIMEOUT_MSG);
            msg.obj = proc;
            mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
                    ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
        }
    }
    //当时间达到了server超时时间,则进入此处
    if (anrMessage != null) {
        //最终通过此处处理ANR
        mAm.mAnrHelper.appNotResponding(proc, anrMessage);
    }
}
复制代码

frameworks/base/services/core/java/com/android/server/am/AnrHelper.java

class AnrHelper {
    private static class AnrRecord {
        ····
        final ProcessRecord mApp;
        ····
        void appNotResponding(boolean onlyDumpSelf) {
            //通过一系列操作最终调用ProcessRecord对象的appNotResponding方法
            mApp.appNotResponding(mActivityShortComponentName, mAppInfo,
                    mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,
                    onlyDumpSelf);
        }
    }
}
复制代码

frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java

void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
                      String parentShortComponentName, WindowProcessController parentProcess,
                      boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
    ····
    synchronized (mService) {
        ····
        //mService对应的是ActivityManagerService对象
        if (mService.mUiHandler != null) {
            // Bring up the infamous App Not Responding dialog
            Message msg = Message.obtain();
            msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
            msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
            //向UiHandler发送一条数据
            mService.mUiHandler.sendMessage(msg);
        }
    }
}
复制代码

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

final class UiHandler extends Handler {
    ···
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            ····
            case SHOW_NOT_RESPONDING_UI_MSG: {
                //同Handler机制回调到此处弹起ANR窗口
                mAppErrors.handleShowAnrUi(msg);
                ensureBootCompleted();
            } break;
            ·····
        }
    }
}
复制代码
(2)、移除Server超时任务

frameworks/base/core/java/android/app/ActivityThread.java

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

class H extends Handler {
    public void handleMessage(Message msg) {
        ····
        switch (msg.what) {
            ····
            case CREATE_SERVICE:
                ····
                //主线程处理Server启动
                handleCreateService((CreateServiceData)msg.obj);
                ····
                break;
            ····
        }
}

private void handleCreateService(CreateServiceData data) {
    ····
    try {
        ····
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        //调用Server生命周期方法
        service.onCreate();
        ···
        try {
            //调用AMS的serviceDoneExecuting方法移除Server超时监测任务
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (Exception e) {
        ····
    }
}
复制代码

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
    synchronized(this) {
        //mServices对应的是ActiveServices对象,移除超时任务
        mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
    }
}
复制代码

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
    ····
    if (r != null) {
        ···· 
        //移除超时任务
        serviceDoneExecutingLocked(r, inDestroying, inDestroying);
        ····
    }
    ···
}

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
                                        boolean finishing) {
    ····
    if (r.executeNesting <= 0) {
        if (r.app != null) {
        ···
            if (r.app.executingServices.size() == 0) {
            ····
                //向MainHandler移除SERVICE_TIMEOUT_MSG的超时任务
                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
            }
        ····
        }
    ····
    }
}
复制代码

32、Handler中的runWithScissors()方法有什么作用?注意事项?

一个线程,调用runWithScissors()方法,就是该线程需要等待runWithScissors()方法内部执行完任务后才能继续执行,执行runWithScissors()方法里的任务可以是其他线程,取决于Handler对象中设置的Looper是在什么线程上

public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
    ····
    BlockingRunnable br = new BlockingRunnable(r);
    return br.postAndWait(this, timeout);
}

private static final class BlockingRunnable implements Runnable {
    private final Runnable mTask;
    private boolean mDone;

    public BlockingRunnable(Runnable task) {
        mTask = task;
    }

    @Override
    public void run() {
        try {
            //执行任务
            mTask.run();
        } finally {
            synchronized (this) {
                //任务是否执行完成
                mDone = true;
                notifyAll();
            }
        }
    }

    public boolean postAndWait(Handler handler, long timeout) {
        //在Handler中的Looper循环消息队列中执行消息
        if (!handler.post(this)) {
            return false;
        }

        synchronized (this) {
            //判断设置规定时间
            if (timeout > 0) {
                final long expirationTime = SystemClock.uptimeMillis() + timeout;
                //根据任务是否完成退出循环等待
                while (!mDone) {
                    //是否在规定时间内完成任务,规定时间未完成直接返回false
                    long delay = expirationTime - SystemClock.uptimeMillis();
                    if (delay <= 0) {
                        return false; // timeout
                    }
                    try {
                        wait(delay);
                    } catch (InterruptedException ex) {
                    }
                }
            } else {
                //根据任务是否完成退出循环等待
                while (!mDone) {
                    try {
                        wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }
        }
        return true;
    }
}
复制代码

问题:

  • 指定的超时时间超时了,消息循环队列没有执行到任务就postAndWait()返回false退出了,而任务还在消息队列中没有被移除,最终会被消息队列执行到但却是无意义的操作
  • 当加入消息队列的消息没有执行到,Looper循环被退出或者消息队列消息被移除了,此时可能会处于上述代码的While无限循环中,造成调用线程进入阻塞,而得不到唤醒,如果当前持有别的锁,还会造成死锁。

安全使用条件:

  • Handler 的 Looper 不允许退出,例如 Android 主线程 Looper 就不允许退出;
  • Looper 退出时,使用安全退出 quitSafely() 方式退出;
文章分类
Android
文章标签