Systrace6 Input 解读

一 概述

本文是 Systrace 系列文章的第六篇,主要是对 Systrace 中的 Input 进行简单介绍,介绍其 Input 的流程; Systrace 中 Input 信息的体现 ,以及如何结合 Input 信息,分析与 Input 相关的问题。

在 Android 基于 Choreographer 的渲染机制详解 这篇文章中,我们知道,Android App 的主线程运行的本质是靠 Message 驱动的,这个 Message 可以是循环动画、可以是定时任务、可以是其他线程唤醒,不过我们最常见的还是 Input Message ,这里的 Input 是以 InputReader 里的分类,不仅包含触摸事件(Down、Up、Move) , 可包含 Key 事件(Home Key 、 Back Key) 。这里我们着重讲的是触摸事件。

由于 Android 系统在 Input 链上加了一些 Trace 点,且这些 Trace 点也比较完善,部分厂家可能会自己加一些,不过我们这里以标准的 Trace 点来讲解,这样不至于你换了个手机抓的 Trace 就不一样了

Input 在 Android 中的地位是很高的,我们在玩手机的时候,大部分应用的滑动、跳转这些都依靠 Input 事件来驱动,后续我会专门写一篇文章,来介绍 Android 中基于 Input 的运行机制。这里是从 Systrace 的角度来看 Input 。看下面的流程之前,脑子里先有个关于 Input 的大概处理流程,这样看的时候,就可以代入:

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

另外在看 Systrace 的时候,要牢记 Systrace 中时间是从左到右流逝的,也就是说如果你在 Systrace 上画一条竖直线,那么竖直线左边的事件永远比右边的事件先发生,这也是我们分析源码流程的一个基石。我希望大家在看基于 Systrace 的源码流程分析之后,脑子里有一个图形化的、立体的流程图,你跟的代码走到哪一步了在图形你在脑中可以快速定位出来。

二 Input in Systrace

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

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

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

2.1 InputDown 事件在 SystemServer 的工作流

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

2.2 InputDown 事件在 App 的工作流

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

2.2.1 App 的 Pending 队列

在这里插入图片描述

2.2.2 主线程处理 Input 事件

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

三 关键知识点和流程

从上面的 Systrace 来看,Input 事件的基本流向如下:

  • InputReader 读取 Input 事件
  • InputReader 将读取的 Input 事件放到 InboundQueue 中
  • InputDispatcher 从 InboundQueue 中取出 Input 事件派发到各个 App(连接) 的 OutBoundQueue
  • 同时将事件记录到各个 App(连接) 的 WaitQueue
  • App 接收到 Input 事件,同时记录到 PendingInputEventQueue ,然后对事件进行分发处理
  • App 处理完成后,回调 InputManagerService 将负责监听的 WaitQueue 中对应的 Input 移除

通过上面的流程,一次 Input 事件就被消耗掉了(当然这只是正常情况,还有很多异常情况、细节处理,这里就不细说了,自己看相关流程的时候可以深挖一下) , 那么本节就从上面的关键流中取几个重要的知识点讲解。

3.1 InputReader

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

InputReader Loop 流程如下

  • getEvents:通过 EventHub (监听目录 /dev/input )读取事件放入 mEventBuffer ,而mEventBuffer 是一个大小为256的数组, 再将事件 input_event 转换为 RawEvent
  • processEventsLocked: 对事件进行加工, 转换 RawEvent -> NotifyKeyArgs(NotifyArgs)
  • 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
}

3.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();
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值