Android Handler机制解析

1. 前言

Handler机制在Android系统中具有非常重要的地位,是app程序能够正常运行的“血管”。
Handler是一种消息机制,其作用主要是以下:

  • 跨线程发送消息,实现线程间通信
  • 执行延迟消息

Handler消息机制的核心类如下:

  • Handler。用于将消息放入消息队列并处理Looper传过来的消息。
  • Looper。从消息队里里取出消息并将其传给Handler去处理,消息队列就是在Looper的构造函数里创建的。
  • Message。消息机制中的消息。传输、处理的基本单元。
  • MessageQueue。消息队列。但并没有使用Queue来存储消息,而是使用单向链表的方式,使用Message类中next指向下一个Message

Handler机制包含了java层和native层的代码,我们梳理的时候一并分析。
以下的代码分析基于Android 14的AOSP。

2. Message、Handler、Looper与MessageQueue的创建

Message实现了Parcelable接口,用于支持进程间传输。先看一下有哪些重要的成员变量。

public final class Message implements Parcelable {
    //发送方自定义的消息码,用于给接受者辨别消息用。
    public int what;
    
    //发送方可赋值这个变量以发送给接收方
    public int arg1;
    
    //发送方可赋值这个变量以发送给接收方
    public int arg2;
    
    //发送给接收方的任意类型对象,但如果涉及到跨进程,必须是framework中包含的可序列化对象
    public Object obj;
    
    //用于跨进程通信
    public Messenger replyTo;
    
    //消息的期望送达时间
    public long when;
    
    //存储发送方发送给接收方的数据
    Bundle data;
    
    //接收、处理此消息的Handler实例
    Handler target;
    
    //接受到消息时执行
    Runnable callback;
    
    //指向下一个执行的消息
    Message next;
    
    //已被回收的消息链表
    private static Message sPool;
    ...
    //存储已回收消息的消息池最大大小
    private static final MAX_POOL_SIZE = 50;
    ...
}

Handler类有多种构造函数,常用的如下,在构造函数里就把Looper、MessageQueue实例传进来。

Handler.java
//需要指定一个线程的Looper,用于确定消息循环所在的线程
public Handler(@NonNull Looper looper) {
    this(looper, null, false);
}

//async:是否异步消息,如果是异步消息会优先处理,用于处理一些重要消息
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,
        boolean shared) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
    mIsShared = shared;
}

Looper,用于从消息队列里拿去消息交给Handler处理,每个线程里你可以拥有复数个Handler,但只会有一个Looper对象。以app的主线程创建为例,在ActivityThreadmain方法里创建主线程:

ActivityThread.java
public static void main(String[] args) {
    ...
    //创建主线程的Looper
    Looper.prepareMainLooper();
    ...
    //开始消息循环
    Looper.loop();
}
Looper.java
public static void prepareMainLooper() {
    //先执行prepare方法,当前线程的Looper对象在这个方法里创建
    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));
}

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

如上,Prepare方法里使用了ThreadLocal类来存储、获取当前线程的Looper对象,ThreadLocal使用get方法保证获取到的对象都是当前线程里之前存储过的对象,起到多线程中的数据隔离作用,所以每个线程只会有一个Looper。毕竟线程是操作系统能够进行运算调度的最小单位,如果一个线程有多个Looper,那存储、 拿取、处理消息流程就会乱套了。
Looper创建时,构造函数中同时会创建一个MessageQueue实例:

Looper.java
//quitAllowed:默认都是true,允许当前线程的消息队列销毁,但主线程是false。
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

java层的消息队列MessageQueue创建好了,但还需要再native层也创建一个消息队列NativeMessageQueue,见MessageQueue的构造函数:

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

nativeInit明显是一个native方法,对应的是android_os_MessageQueue_nativeInit方法,里面会创建一个NativeMessageQueue对象,并使用reinterpret_cast将指向该消息队列的指针转换成一个long类型数据返回给java层,后续java就能使用这个值在native层准确引用到该NativeMessageQueue对象。

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

3. 发送消息

官方建议使用Message的内部的静态方法obtain,其从自带的消息池获取Message实例,减少对象创建对性能的消耗,同名方法有好几种,下面是其中一种。

Message.java
public static Message obtain(Handler h, int what,
        int arg1, int arg2, Object obj) {
    Message m = obtain();
    m.target = h;
    m.what = what;
    m.arg1 = arg1;
    m.arg2 = arg2;
    m.obj = obj;

    return m;
}

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

获取到Message实例,可以按照自己业务的要求对whatarg1,arg2,obj等等对象赋值后,开始发送消息。

Handler.java
//有很多发送消息的方法,我选取其中几个常用的
public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}

//delayMillis:延迟处理消息的时长
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

//uptimeMillis:处理消息的目标时间点
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

//将消息加入消息队列
private boolean enqueueMessage(@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);
}

Handler最后的操作就是将Message插入到MessageQueue里面去,

MessageQueue.java
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.");
        }
        //该消息队列处于销毁中,则返回
        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;
        //如果1.当前消息队列无消息。2.预定处理消息时间点为0即立即执行。3.预定处理消息时间点小于队首消息时间点
        //以上满足任一条件,则将该消息插队为队首消息
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            //如果当前消息队列处于阻塞中,则需要进行唤醒
            needWake = mBlocked;
        } else {
            //如果当前队列阻塞中,而队首消息无Handler去处理且此消息是异步消息,则需要唤醒
            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;
        }
        //如果需要唤醒,执行native方法nativeWake,mPtr指向native层的NativeMessageQueue
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

最后调用了nativeWake的本地方法,

android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}

使用了reinterpret_cast方法将long类型的mPtr强转换成指向NativeMessageQueue的指针,NativeMessageQueue就是在创建java层MessageQueue时,在构造函数里调用nativeInit本地方法创建的native层的消息队列。android_os_MessageQueue_nativeWake方法调用了nativeMessageQueuewake方法。

android_os_MessageQueue.cpp
void NativeMessageQueue::wake() {
    mLooper->wake();
}
/system/core/libutils/Looper.cpp
void Looper::wake() {
    ...
    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
      if (errno != EAGAIN) {
          LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
                           mWakeEventFd.get(), nWrite, strerror(errno));
      }
    }
}

通过向mWakeEventFd文件描述符写入一个1,表示写入一个事件,达到唤醒的效果,那消息队列是在哪里阻塞住的呢?即在哪里等待被唤醒的呢?

4. 消息循环,读取消息

答案是之前执行Looperloop方法开启消息循环读取时,见上面的主线程looper创建的代码。

Looper.java
public static void loop() {
    final Looper me = myLooper();
    ...
    me.mInLoop = true;
    ...
    //无限循环地读取、处理消息
    for (;;) {
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

//单次读取消息并处理
private static boolean loopOnce(final Looper me,
    final long ident, final int thresholdOverride) {
    //阻塞式地读取消息,无消息时阻塞,有消息时被唤醒,取到消息
    Message msg = me.mQueue.next(); // might block
    ...
    //msg.target就是发送消息时设置的Handler对象,发给Hander进行处理
    try {
        msg.target.dispatchMessage(msg);
        if (observer != null) {
            observer.messageDispatched(token, msg);
        }
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    } catch (Exception exception) {
        if (observer != null) {
            observer.dispatchingThrewException(token, msg, exception);
        }
        throw exception;
    } finally {
        ThreadLocalWorkSource.restore(origWorkSource);
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);
        }
    }
}

LooperloopOnce方法里主要是执行两个操作:

  • 阻塞式地从MessageQueue中读取消息
  • 将消息交给Handler处理。

我们依次来看,首先会执行MessageQueuenext方法去获取需要现在执行的消息:

//MessageQueue.java
Message next() {
    //又出现一次无限循环
    for (;;) {
        ...
        //native函数,如果当前无消息则会阻塞住,等到超时或者调用nativeWake唤醒时,往下走
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //如果存在异步消息,则立即执行
            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;
                    if (prevMsg != null) {
                        //如果存在异步消息提前取出执行的情况,则将异步消息的前一个指向异步消息的后一个
                        prevMsg.next = msg.next;
                    } else {
                        //正常情况下就将队首消息设置为当前消息的下一个
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                //当前队列没有消息,则超时时间设置为-1,native的队列将一直等待
                nextPollTimeoutMillis = -1;
            }
            ...
        }
        ...
    }
}

nativePollOnce是native方法,最后走到了native层创建的Looper对象的pollOnce方法:

android_os_MessageQueue.java
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对象的pollOnce方法,传进去一个超时时间
    mLooper->pollOnce(timeoutMillis);
    mPollObj = NULL;
    mPollEnv = NULL;

    if (mExceptionObj) {
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    }
}
Looper.cpp
inline int pollOnce(int timeoutMillis) {
    return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
}

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    //无线循环中
    for (;;) {
        ...
        //上次循环的计算结果不为0时,则结束循环,不再阻塞,返回到Java层继续执行后面的代码
        if (result != 0) {
            if (outFd != nullptr) *outFd = 0;
            if (outEvents != nullptr) *outEvents = 0;
            if (outData != nullptr) *outData = nullptr;
            return result;
        }
        //计算本地循环的结果
        result = pollInner(timeoutMillis);
    }
}

int Looper::pollInner(int timeoutMillis) {
    //如果传进来的超时时间不为0,且存在之前计算出来的下个消息预定处理时间点
    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;
        }
    }
    
    //设置本次返回结果为POLL_WAKE
    int result = POLL_WAKE;
    ...
    //先设置当前处于空闲等待状态
    mPolling = true;
    //使用epoll返回事件数量,如果为空则会进入阻塞状态,等待超时被唤醒或者后续由fd文件的写入被唤醒
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    //走到这里,说明当前应该存在事件,走出了阻塞状态
    mPolling = false;
    ...
    //被唤醒后,读取到事件数量却小于0,属于异常
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
        result = POLL_ERROR;
        goto Done;
    }
    //被唤醒后,返回的事件数量为0,说明属于被超时触发的唤醒
    if (eventCount == 0) {
        result = POLL_TIMEOUT;
        goto Done;
    }
    
    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方法里从mWakeEventFd中读取出之前写入的值,表示已消费该事件
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
            ...
        }
    }
    ...
    return result;
   
}

void Looper::awoken() {
    uint64_t counter;
    TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}

native层的Looper使用epoll_wait方式进行阻塞等待。如果当前时间点没有事件需要处理,则会开始阻塞,等待预先设定的超时时间到来或者被mWakeEventFd文件描述符的写入唤醒,并从中读取之前在wake中写入的1,表示将该事件进行消费。接着这个方法执行完毕,就会回到java层,执行后续的处理消息的流程。epoll_wait具体是怎么做到阻塞的可以网上查查大佬们的解释,本愚弟就不出丑了。

5. 处理消息

继续回到上面LooperloopOnce方法,当在MessageQueuenext方法里取出一个消息后,Looper便将这个Message交给HandlerdispatchMessage方法去执行。

Handler.java
public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        //如果设置了callback,则直接执行
        handleCallback(msg);
    } else {
        //是否有自定义的回调,这个回调在构造函数里传入
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //没有自定义回调,则调用默认的空方法,开发可以写个自定义类继承Handler并重写这个方法
        handleMessage(msg);
    }
}

就此,一个消息从发送到排队到被取出处理的过程基本梳理完了。

  • 18
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值