Native looper 分析

2024-05-07 05:32
文章标签 分析 looper native

本文主要是介绍Native looper 分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自:http://blog.csdn.net/windskier/article/details/6995546

Looper是android中很重要的概念,它是android application端线程间最主要的通信方式,同时它也是线程内部序列化处理的主要方式,Looper的核心其实是一个消息队列,通过不停的处理Looper消息队列中的消息来完成线程间的通信和线程内部序列化操作。任何线程想要使用消息机制特定的操作,那么必须在线程中创建一个Looper,java端的Looper如何使用不介绍了,所有有过android开发经验的人都知道怎么用,这篇文章主要介绍一下Native Looper,看它如何和JAVA层的Looper相互配合完成android中最主要的线程通信机制的。同时写这篇的目的是为了解释文章中事件传递过程中,native Looper是如何被复用实现管道通信的。

    上面说到JAVA Looper的核心其实是一个消息队列,并且我们分析一下Looper.java的代码,并没有任何和Native有关联的数据结构和操作,那么唯一能和Native Looper发生联系的就剩下这个JAVA的消息队列类型MessageQueue了,也果不其然,在MessageQueue中定义了一个成员    

[java] view plain copy
print ?
  1. private int mPtr; // used by native code  
    private int mPtr; // used by native code
它保存着对应的Native的消息队列实例的地址,用一个int类型的成员保存native实例,这是jni开发中常用到的方式。因此MessageQueue同样使用mPtr来表示native的消息队列,NativeMessageQueue@android_os_MessageQueue.cpp,看一下NativeMessageQueue的构造函数,其中定义了一个Native的Looper,

[cpp] view plain copy
print ?
  1. NativeMessageQueue::NativeMessageQueue() {  
  2.     mLooper = Looper::getForThread();  
  3.     if (mLooper == NULL) {  
  4.         mLooper = new Looper(false);  
  5.         Looper::setForThread(mLooper);  
  6.     }  
  7. }  
NativeMessageQueue::NativeMessageQueue() {mLooper = Looper::getForThread();if (mLooper == NULL) {mLooper = new Looper(false);Looper::setForThread(mLooper);}
}
    因此整个的结构很简单,JAVA Looper包含一个MessageQueue,MessageQueue对应的Native 实例是一个NativeMessageQueue实例,NativeMessageQueue在创建的时候生成一个Native Looper。

    其实Native Looper存在的意义就是作为JAVA Looper机制的开关器,

    1. 当消息队列中有消息存入时,唤醒Natvice Looper,至于如何发送向线程消息,这就用到了Handler,google的文档中说明的很详细,不介绍了;

    2. 当消息队列中没有消息时或者消息尚未到处理时间时,Natvice Looper block住整个线程。

    上述功能其实一句话就可以总结:创建了JAVA Looper的线程只有在有消息待处理时才处于活跃状态,无消息时block在等待消息写入的状态。
    下面我们就来分析Natvice Looper它是如何实现这个功能的,先从Natvice Looper入手,看它能干什么。

   1. Native Looper初始化

    我们看一下Natvice Looper的初始化过程都作了那些工作

    1. 创建一个pipe管道mWakeReadPipeFd和mWakeWritePipeFd,这个管道的作用就是为了将block住的线程唤醒。

    2. 创建一个epoll实例mEpollFd,用它来监听event触发,event有mWakeReadPipeFd上的 wake event;还有上一篇文章当中讲到的NativeInputQueue和InputDispatcher模块注册在各自Looper中,需要监听的的硬件设备的事件发生时的通知event以及事件消化完的通知event。

   在构造函数中只向mEpollFd添加对mWakeReadPipeFd的监听,NativeInputQueue和InputDispatcher模块注册的监听需要通过addFd()方法在各自的模块中注册,目前来看只有NativeInputQueue真正使用到当前线程的Native Looper,而InputDispatcher是自定义了一个Native Looper,参考上一篇文章。

[cpp] view plain copy
print ?
  1. Looper::Looper(bool allowNonCallbacks) :  
  2.         mAllowNonCallbacks(allowNonCallbacks),  
  3.         mResponseIndex(0) {  
  4.     int wakeFds[2];  
  5.     int result = pipe(wakeFds);  
  6.     LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);  
  7.   
  8.     mWakeReadPipeFd = wakeFds[0];  
  9.     mWakeWritePipeFd = wakeFds[1];  
  10.   
  11.     result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);  
  12.     LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",  
  13.             errno);  
  14.   
  15.     result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);  
  16.     LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",  
  17.             errno);  
  18.   
  19. #ifdef LOOPER_USES_EPOLL  
  20.     // Allocate the epoll instance and register the wake pipe.  
  21.     mEpollFd = epoll_create(EPOLL_SIZE_HINT);  
  22.     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);  
  23.   
  24.     struct epoll_event eventItem;  
  25.     memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union  
  26.     eventItem.events = EPOLLIN;  
  27.     eventItem.data.fd = mWakeReadPipeFd;  
  28.     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);  
  29.     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",  
  30.             errno);  
  31. #else  
  32.     ...........................................   
  33. }  
Looper::Looper(bool allowNonCallbacks) :mAllowNonCallbacks(allowNonCallbacks),mResponseIndex(0) {int wakeFds[2];int result = pipe(wakeFds);LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);mWakeReadPipeFd = wakeFds[0];mWakeWritePipeFd = wakeFds[1];result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",errno);result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",errno);#ifdef LOOPER_USES_EPOLL// Allocate the epoll instance and register the wake pipe.mEpollFd = epoll_create(EPOLL_SIZE_HINT);LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);struct epoll_event eventItem;memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field unioneventItem.events = EPOLLIN;eventItem.data.fd = mWakeReadPipeFd;result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",errno);
#else........................................... 
}

 2. Native Looper的业务逻辑

    这一部分我们深入分析一下Native Looper作为JAVA Looper机制的开关控制器的。

    JAVA Looper通过调用loop()不断的去检测消息队列中是否有消息需要处理,调用的是MessageQueue的next()方法,这个方法返回的是MessageQueue的存储的消息。


    loop()@Looper.java

[java] view plain copy
print ?
  1. Message msg = queue.next(); // might block  
   Message msg = queue.next(); // might block


[java] view plain copy
print ?
  1. final Message next() {  
  2.     int pendingIdleHandlerCount = -1// -1 only during first iteration  
  3.     int nextPollTimeoutMillis = 0;  
  4.   
  5.     for (;;) {  
  6.         if (nextPollTimeoutMillis != 0) {  
  7.             Binder.flushPendingCommands();  
  8.         }  
  9.         nativePollOnce(mPtr, nextPollTimeoutMillis);  
  10.   
  11.         synchronized (this) {  
  12.             // Try to retrieve the next message.  Return if found.  
  13.             final long now = SystemClock.uptimeMillis();  
  14.             final Message msg = mMessages;  
  15.             if (msg != null) {  
  16.                 final long when = msg.when;  
  17.                 if (now >= when) {  
  18.                     mBlocked = false;  
  19.                     mMessages = msg.next;  
  20.                     msg.next = null;  
  21.                     if (Config.LOGV) Log.v("MessageQueue""Returning message: " + msg);  
  22.                     return msg;  
  23.                 } else {  
  24.                     nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);  
  25.                 }  
  26.             } else {  
  27.                 nextPollTimeoutMillis = -1;  
  28.             }  
  29.   
  30.             // If first time, then get the number of idlers to run.  
  31.             if (pendingIdleHandlerCount < 0) {  
  32.                 pendingIdleHandlerCount = mIdleHandlers.size();  
  33.             }  
  34.             if (pendingIdleHandlerCount == 0) {  
  35.                 // No idle handlers to run.  Loop and wait some more.  
  36.                 mBlocked = true;  
  37.                 continue;  
  38.             }  
  39.   
  40.             if (mPendingIdleHandlers == null) {  
  41.                 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];  
  42.             }  
  43.             mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);  
  44.         }  
  45.   
  46.         // Run the idle handlers.  
  47.         // We only ever reach this code block during the first iteration.  
  48.         for (int i = 0; i < pendingIdleHandlerCount; i++) {  
  49.             final IdleHandler idler = mPendingIdleHandlers[i];  
  50.             mPendingIdleHandlers[i] = null// release the reference to the handler  
  51.   
  52.             boolean keep = false;  
  53.             try {  
  54.                 keep = idler.queueIdle();  
  55.             } catch (Throwable t) {  
  56.                 Log.wtf("MessageQueue""IdleHandler threw exception", t);  
  57.             }  
  58.   
  59.             if (!keep) {  
  60.                 synchronized (this) {  
  61.                     mIdleHandlers.remove(idler);  
  62.                 }  
  63.             }  
  64.         }  
  65.   
  66.         // Reset the idle handler count to 0 so we do not run them again.  
  67.         pendingIdleHandlerCount = 0;  
  68.   
  69.         // While calling an idle handler, a new message could have been delivered  
  70.         // so go back and look again for a pending message without waiting.  
  71.         nextPollTimeoutMillis = 0;  
  72.     }  
  73. }  
    final Message next() {int pendingIdleHandlerCount = -1; // -1 only during first iterationint nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}nativePollOnce(mPtr, nextPollTimeoutMillis);synchronized (this) {// Try to retrieve the next message.  Return if found.final long now = SystemClock.uptimeMillis();final Message msg = mMessages;if (msg != null) {final long when = msg.when;if (now >= when) {mBlocked = false;mMessages = msg.next;msg.next = null;if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);return msg;} else {nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);}} else {nextPollTimeoutMillis = -1;}// If first time, then get the number of idlers to run.if (pendingIdleHandlerCount < 0) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount == 0) {// No idle handlers to run.  Loop and wait some more.mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// Run the idle handlers.// We only ever reach this code block during the first iteration.for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try {keep = idler.queueIdle();} catch (Throwable t) {Log.wtf("MessageQueue", "IdleHandler threw exception", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}// Reset the idle handler count to 0 so we do not run them again.pendingIdleHandlerCount = 0;// While calling an idle handler, a new message could have been delivered// so go back and look again for a pending message without waiting.nextPollTimeoutMillis = 0;}}

    在获取MessageQueue中消息过程中,我们发现next()是一个循环,除了获取消息队列之外,最重要的一点就是去监听Natvie Looper的event触发,调用nativePollOnce(mPtr, nextPollTimeoutMillis); 它最终会调用到pollInner()@Looper.cpp。我们来看看pollInner都干了些什么?

    1. 等待mEpollFd的事件触发,我们前面说过,这个事件触发有两种,第一个就是唤醒Native Looper的wake消息,另外一个就是复用Native Looper的其他消息,如NativeInputQueue和InputDispatcher的管道检测(目前android也就这两个模块使用到了)。

当epoll_wait()的等待时间不为0时,即JAVA Looper传递下来的nextPollTimeoutMillis值,那么整个线程就被block在这儿了。

    pollInner()@Looper.cpp

[cpp] view plain copy
print ?
  1. struct epoll_event eventItems[EPOLL_MAX_EVENTS];  
  2. int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);  
  3. bool acquiredLock = false;  
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);bool acquiredLock = false;
   2. 如果有事件触发发生,wake或者其他复用Looper的event,处理event,这样整个Native Looper就从block状态中解脱出来了,整个线程也就解脱出来了,JAVA Looper则会执行nativePollOnce(mPtr, nextPollTimeoutMillis);下面的语句,next()@MessageQueue.java后面的语句就不解释了。

    我们只需要注意nextPollTimeoutMillis值的设置,如果消息尚未到达处理时间,则nextPollTimeoutMillis值则为距离该消息处理时间的总时长,表明Native Looper只需要block到消息需要处理的时间就行了。如果没有消息待处理,那么将一直Native Looper将一直block住,等待wake event。

    那么什么时候会有wake event发生呢?只有在有新的消息被存储到MessageQueue时,会向Native Looper发起wake event。

    enqueueMessage()@MessageQueue.java

[cpp] view plain copy
print ?
  1. if (needWake) {  
  2.     nativeWake(mPtr);  
  3. }  
        if (needWake) {nativeWake(mPtr);}
     整个Native Looper的基本机制就是这样的,保证在线程无消息可处理时,能够尽可能的减少CPU的利用,将宝贵的CPU资源交给其他线程或者进程处理,这一点在移动设备中是很重要的。

 3. Native Looper扩展应用

Native Looper尽管通常情况下是和JAVA 层的Looper和MessageQueue配合使用的,作为JAVA Looper的开关控制器存在的,但是鉴于上面我们对Native Looper的分析,我们发现在native code开发时,我们完全也可以单独使用Native Looper,比如说在开发的native code中如果使用到了pipe或者socket通信的话,Native Looper将会是把利器,我们通过它能够很好的管理pipe或者socket等的通信。正如InputDispatcher所做的一样

这篇关于Native looper 分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/966453

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

PostgreSQL核心功能特性与使用领域及场景分析

PostgreSQL有什么优点? 开源和免费 PostgreSQL是一个开源的数据库管理系统,可以免费使用和修改。这降低了企业的成本,并为开发者提供了一个活跃的社区和丰富的资源。 高度兼容 PostgreSQL支持多种操作系统(如Linux、Windows、macOS等)和编程语言(如C、C++、Java、Python、Ruby等),并提供了多种接口(如JDBC、ODBC、ADO.NET等