Handler底层机制

Handler底层机制_第1张图片
 安卓的底层是Linux系统,了解Android Handler底层之前,需要了解一下Linux 知识。

  • 文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符。

  • epoll:IO多路复用机制,可以理解为event poll,不同于忙轮询和无差别轮询,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作)。
     每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。epoll都是使用waitqueue调用callback函数去wakeup你的异步等待线程的,如果设置了timeout的话就起一个timer,epoll的waitqueue callback函数把当前的有效fd加到ready list,然后唤醒异步等待进程,所以epoll函数返回的就是这个ready list。ready list中包含所有有效的fd,所以kernel不用去遍历所有的fd,用户空间程序也不用遍历所有的fd,而只是遍历返回有效fd链表。

  • pipe:管道,使用I/O流操作,Linux 系统中的一种进程间通信机制。简单来说,管道就是一个文件,在管道的两端,分别是两个打开文件的文件描述符。这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的,一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容。当管道没有内容时,这个线程就会进入等待状态,而另外一个线程通过写文件描述符来向管道中写入内容。写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。在管道机制的实现中,又使用 epoll 机制来监听读写事件。

归入主题,handler底层是怎么运作的呢?

MessageQueue的底层实现是利用管道和epoll机制来实现的。

Handler底层机制_第2张图片
 MessageQueue 在构造方法中,会调用 native 方法 nativeInit 方法,在NativeMessageQueue 的构造方法中,会构造一个 JNI 层的 Looper。Looper.loop()有个for死循环,它调用了MessageQueue下的next方法。
Handler底层机制_第3张图片

Handler底层机制_第4张图片
 如上图,nextPollTimeoutMillis这个变量,这个变量代表MessageQueue下次被唤醒的时间。MessageQueue里Message在加入队列的时候,会按照执行的时间顺序排列;每次消息入队列时,MessageQueue都会尽量计算出一个精确的时间。假如这个时间是计算出来是1000ms,如果消息队列中没有消息需要马上处理时,会判断用户是否设置了Idle Handler。如果有的话,则会尝试处理mIdleHandlers中所记录的所有Idle Handler。此时会逐个调用这些Idle Handler的queueIdle()成员函数,再次调用nativePollOnce()方法,线程阻塞住,不占用资源。当时间到了,会往管道流中写入字节流,唤醒线程,处理Message。

Looper队列的阻塞唤醒的功能是怎么实现的?

 MessageQueue 是按照消息触发时间的先后顺序排列的,队列头部的消息是最早触发的。当有消息加入,会从队列头部开始遍历,插入到合适的位置,以保证所有消息的时间顺序。如果当前线程处于空闲等待状态,需要调用 nativeWake 来唤醒。

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) {
    // ptr 获取 NativeMessageQueue
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);  
    return nativeMessageQueue->wake();  
}

唤醒请求转发到 Looper wake,往管道写入内容,从而唤醒线程。

void Looper::wake() {  
    ......  
  
    ssize_t nWrite;  
    do {
        nWrite = write(mWakeWritePipeFd, "W", 1);  // 先管道中写入 "W
    } while (nWrite == -1 && errno == EINTR);  
  
    .......  
}

当消息队列中没有消息处理时,线程会进入空闲等待状态,具体是通过 Looper 调用 epoll_wait。
附图,
Handler底层机制_第5张图片

  • 总结:
     线程在进入循环之前,会在 JNI 创建管道,当消息队列为空时,线程处于空闲等待状态。通过 epoll 机制监听 EPOLLIN 事件,当有新事件进入消息队列时,并且当前线程处于空闲状态,通过向管道写入数据,来唤醒线程。

你可能感兴趣的:(android,android,android,studio)