Systrace 基础知识 - Input 解读

参考连接:https://www.androidperformance.com/2019/11/04/Android-Systrace-Input/

1.概述

Android 中 Input 的大概处理流程:

  1. 触摸屏每隔几毫秒扫描一次,如果有触摸事件,那么把事件上报到对应的驱动
  2. InputReader 读取触摸事件交给
  3. InputDispatcher 进行事件派发 InputDispatcher 将触摸事件发给注册了 Input 事件的 App App
  4. 拿到事件之后,进行 Input 事件分发,如果此事件分发的过程中,App 的 UI 发生了变化,那么会请求 Vsync,则进行一帧的绘制

在看 Systrace 的时候,要牢记 Systrace 中时间是从左到右流逝的。下面这张图是一个概览图,以滑动桌面为例 (滑动桌面包括一个 Input_Down 事件 + 若干个 Input_Move 事件 + 一个 Input_Up 事件,这些事件和事件流都会在 Systrace 上有所体现,这也是我们分析 Systrace 的一个重要的切入点),主要牵扯到的模块是 SystemServer 和 App 模块,其中用蓝色标识的是事件的流动信息,红色的是辅助信息。
在这里插入图片描述
InputReader 和 InputDispatcher 是跑在 SystemServer 里面的两个 Native 线程,负责读取和分发 Input 事件,我们分析 Systrace 的 Input 事件流,首先是找到这里。下面针对上图中标号进行简单

说明

  1. InputReader 负责从 EventHub 里面把 Input 事件读取出来,然后交给 InputDispatcher进行事件分发
  2. InputDispatcher 在拿到 InputReader 获取的事件之后,对事件进行包装和分发 (也就是发给对应的)
  3. OutboundQueue 里面放的是即将要被派发给对应 AppConnection 的事件 WaitQueue 里面记录的是已经派发给
  4. AppConnection 但是 App 还在处理没有返回处理成功的事件
  5. PendingInputEventQueue 里面记录的是App 需要处理的 Input 事件,这里可以看到已经到了应用进程
  6. deliverInputEvent 标识 App UI Thread被 Input 事件唤醒
  7. InputResponse 标识 Input 事件区域,这里可以看到 Input_Down 事件 + 若干个Input_Move 事件 + 一个 Input_Up 事件这段都被算到了这里
  8. App 响应 Input 事件 : 这里是滑动然后松手,也就是我们熟悉的桌面滑动的操作,桌面随着手指的滑动更新画面,松手后触发 Fling 继续滑动,从 Systrace
    就可以看到整个事件的流程

下面以第一个 Input_Down 事件的处理流程来进行详细的工作流说明,其他的 Move 事件和 Up 事件的处理是一样的(部分不一样,不过影响不大)

2.InputDown 事件在 SystemServer 的工作流

放大 SystemServer 的部分,可以看到其工作流(蓝色),滑动桌面包括 Input_Down + 若干个 Input_Move + Input_Up ,我们这里看的是 Input_Down 这个事件
在这里插入图片描述

3.InputDown 事件在 App 的工作流

应用在收到 Input 事件后,有时候会马上去处理 (没有 Vsync 的情况下),有时候会等 Vsync 信号来了之后取处理,这里 Input_Donw 事件就是直接去唤醒主线程做处理,其 Systrace 比较简单,最上面有个 Input 事件队列,主线程则是简单的处理在这里插入图片描述

4.App 的 Pending 队列

在这里插入图片描述

5.主线程处理 Input 事件

主线程处理 Input 事件这个大家比较熟悉,从下面的调用栈可以看到,Input 事件传到了 ViewRootImpl,最终到了 DecorView ,然后就是大家熟悉的 Input 事件分发机制
在这里插入图片描述

6.关键知识点和流程

6.1 InputReader

InputReader 是一个 Native 线程,跑在 SystemServer 进程里面,其核心功能是从 EventHub 读取事件、进行加工、将加工好的事件发送到 InputDispatcher

InputReader Loop 流程如下
1.getEvents:通过 EventHub (监听目录 /dev/input )读取事件放入 mEventBuffer ,而mEventBuffer 是一个大小为256的数组, 再将事件 input_event 转换为 RawEvent
2.processEventsLocked: 对事件进行加工, 转换 RawEvent -> NotifyKeyArgs(NotifyArgs)
3.QueuedListener->flush:将事件发送到 InputDispatcher 线程, 转换 NotifyKeyArgs -> KeyEntry(EventEntry)

核心代码 loopOnce 处理流程如下:
在这里插入图片描述

InputReader 核心 Loop 函数 loopOnce 逻辑如下

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    { // acquire lock
    ......
    //获取输入事件、设备增删事件,count 为事件数量
    size_t count = mEventHub ->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    {
    ......
        if (count) {//处理事件
            processEventsLocked(mEventBuffer, count);
        }

    }
    ......
    mQueuedListener->flush();//将事件传到 InputDispatcher,这里getListener 得到的就是 InputDispatcher
}

6.2 InputDispatcher

上面的 InputReader 调用 mQueuedListener->flush 之后 ,将 Input 事件加入到InputDispatcher 的 mInboundQueue ,然后唤醒 InputDispatcher , 从 Systrace 的唤醒信息那里也可以看到 InputDispatch 线程是被 InputReader 唤醒的
在这里插入图片描述

InputDispatcher 的核心逻辑如下:
dispatchOnceInnerLocked(): 从 InputDispatcher 的 mInboundQueue 队列,取出事件 EventEntry。另外该方法开始执行的时间点 (currentTime) 便是后续事件 dispatchEntry 的分发时间 (deliveryTime)
dispatchKeyLocked():满足一定条件时会添加命令 doInterceptKeyBeforeDispatchingLockedInterruptible;
enqueueDispatchEntryLocked():生成事件 DispatchEntry 并加入 connection 的 outbound 队列
startDispatchCycleLocked():从 outboundQueue 中取出事件 DispatchEntry, 重新放入 connection 的 waitQueue 队列;
InputChannel.sendMessage 通过 socket 方式将消息发送给远程进程;
runCommandsLockedInterruptible():通过循环遍历地方式,依次处理 mCommandQueue 队列中的所有命令。而 mCommandQueue 队列
中的命令是通过 postCommandLocked() 方式向该队列添加的。
在这里插入图片描述
其核心处理逻辑在 dispatchOnceInnerLocked 这里

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    // Ready to start a new event.
    // If we don't already have a pending event, go grab one.
    if (! mPendingEvent) {
        if (mInboundQueue.isEmpty()) {
        } else {
            // Inbound queue has at least one entry.
            mPendingEvent = mInboundQueue.dequeueAtHead();
            traceInboundQueueLengthLocked();
        }

        // Poke user activity for this event.
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(mPendingEvent);
        }

        // Get ready to dispatch the event.
        resetANRTimeoutsLocked();
    }
    case EventEntry::TYPE_MOTION: {
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }

    if (done) {
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;
        releasePendingEventLocked();
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
}

6.3 InboundQueue

InputDispatcher 执行 notifyKey 的时候,会将 Input 事件封装后放到 InboundQueue 中,后续 InputDispatcher 循环处理 Input 事件的时候,就是从 InboundQueue 取出事件然后做处理
在这里插入图片描述

6.4 OutboundQueue

Outbound 意思是出站,这里的 OutboundQueue 指的是要被 App 拿去处理的事件队列,每一个 App(Connection) 都对应有一个 OutboundQueue ,从 InboundQueue 那一节的图来看,事件会先进入 InboundQueue ,然后被 InputDIspatcher 派发到各个 App 的 OutboundQueue
在这里插入图片描述

6.5 WaitQueue

当 InputDispatcher 将 Input 事件分发出去之后,将 DispatchEntry 从 outboundQueue 中取出来放到 WaitQueue 中,当 publish 出去的事件被处理完成(finished),InputManagerService 就会从应用中得到一个回复,此时就会取出 WaitQueue 中的事件,从 Systrace 中看就是对应 App 的 WaitQueue 减少

如果主线程发生卡顿,那么 Input 事件没有及时被消耗,也会在 WaitQueue 这里体现出来,如下图:
在这里插入图片描述

7.整体逻辑

图来自 Gityuan 博客
在这里插入图片描述

8.Input 刷新与 Vsync

Input 的刷新取决于触摸屏的采样,目前比较多的屏幕采样率是 120Hz 和 160Hz ,对应就是 8ms 采样一次或者 6.25ms 采样一次,我们来看一下其在 Systrace 上的展示
在这里插入图片描述可以看到上图中, InputReader 每隔 6.25ms 就可以读上来一个数据,交给 InputDispatcher 去分发给 App ,那么是不是屏幕采样率越高越好呢?也不一定,比如上面那张图,虽然 InputReader 每隔 6.25ms 就可以读上来一个数据给 InputDispatcher 去分发给 App ,但是从 WaitQueue 的表现来看,应用并没有消耗这个 Input 事件,这是为什么呢?

原因在于应用消耗 Input 事件的时机是 Vsync 信号来了之后,刷新率为 60Hz 的屏幕,一般系统也是 60 fps ,也就是说两个 Vsync 的间隔在 16.6ms ,这期间如果有两个或者三个 Input 事件,那么必然有一个或者两个要被抛弃掉,只拿最新的那个。也就是说:

在屏幕刷新率和系统 FPS 都是 60 的时候,盲目提到触摸屏的采样率,是没有太大的效果的,反而有可能出现上面图中那样,有的 Vsync 周期中有两个 Input 事件,而有的 Vsync 周期中有三个 Vsync 事件,这样造成事件不均匀,可能会使 UI 产生抖动
在屏幕刷新率和系统 FPS 都是 60 的时候,使用 120Hz 采样率的触摸屏就可以了,如果在屏幕刷新率和系统 FPS 都是 90 的时候 ,那么 120Hz 采样率的触摸屏显然不够用了,这时候应该采用 180Hz 采样率的屏幕

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android Systrace是一种用于分析Android系统性能的工具。它可以捕获系统的各种事件和跟踪信息,并将其可视化为时间轴图形,以便开发人员更好地了解系统的行为和性能瓶颈。Systrace可以用于分析应用程序、系统服务和内核模块的性能问题,帮助开发人员优化应用程序和系统性能Systrace的使用需要一定的技术和经验,但是掌握了基础知识后,可以大大提高开发效率和应用程序性能。 ### 回答2: Android systrace 是一个用于分析和优化 Android 系统性能的工具。它可以帮助开发者检测出系统中的性能问题并提供解决方案。以下是关于 systrace 的一些基础知识: 1. systrace 的作用:systrace 可以跟踪和显示系统的各个组件之间的交互,并提供详细的性能数据。通过分析 systrace 数据,开发者可以定位和解决应用程序在运行过程中遇到的性能瓶颈问题。 2. systrace 的使用方法:通过命令行或 Android Studio 中的性能分析工具可以使用 systrace。开发者可以选择要捕获的系统事件和应用程序组件,然后生成一份包含性能数据的报告。报告中包含的信息有 CPU 使用率、线程活动、绘制时间、电源管理、内存使用等。 3. systrace 的视图:systrace 报告包含多个视图,每个视图都显示不同的性能数据。常用的视图包括 CPU、渲染、电源、内存和启动视图。开发者可以根据需求选择并分析相应的视图。 4. systrace 的优化策略:通过 systrace 分析数据,开发者可以找到系统性能的瓶颈,并通过一些优化策略来提升性能。例如,优化 CPU 使用率、减少线程活动、优化绘制时间、减少内存使用等。 总之,Android systrace 是一种强大的分析工具,能够帮助开发者检测和解决 Android 系统的性能问题。掌握 systrace 的使用方法和相关的性能优化策略,对于开发高质量的 Android 应用程序非常重要。 ### 回答3: Android Systrace是一个用于分析和优化Android系统性能的工具。它通过收集系统的实时跟踪数据来显示对系统资源的使用和各个进程、线程的活动情况进行分析。 使用Systrace前,需要在设备上启用“开发者选项”并连接到计算机上。然后,在命令行中运行"adb shell"进入设备的shell环境,使用"atrace"命令来启动SystraceSystrace会将跟踪数据保存到一个trace文件中。 Systrace可以用于分析多个方面的性能问题,包括CPU使用率、渲染性能、I/O操作、电池使用情况等。通过图形界面或命令行工具,可以查看trace文件并找出性能瓶颈所在。 Systrace的图形界面提供了一些工具和选项来帮助分析性能问题。例如,可以通过选择不同的时间段来查看特定时间段内的性能数据,也可以使用颜色编码和热力图来显示不同进程和线程的活动情况。 在使用Systrace进行性能优化时,需要注意以下几点: 1. 确定关注的性能指标,例如CPU使用率、内存占用等。 2. 分析trace文件时,要注意特定进程和线程的活动情况,找出可能的性能瓶颈。 3. 通过与其他工具(如Android Studio的Profiler)结合使用,可以更全面地分析和优化系统性能。 总之,Android Systrace是一个强大的性能分析工具,能够帮助开发者发现和解决Android系统中的性能问题,提高应用的用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值