Input系统介绍

1. 启动篇

当用户触摸屏幕或者按键操作,首次触发的是 硬件驱动,驱动收到事件后,将该相应事件写入到输入设备节点, 这便产生了最原生态的内核事件。接着,输入系统取出原生态的事件,经过层层封装后成为KeyEvent或者MotionEvent ;最后,交付给相应的目标窗口(Window)来消费该输入事件。可见,输入系统在整个过程起到承上启下的衔接作用

1.1 InputDispatcher

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
    mPolicy(policy),
    mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
    mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
    //创建Looper对象
    mLooper = new Looper(false);

    mKeyRepeatState.lastKeyEntry = NULL;
    //获取分发超时参数
    policy->getDispatcherConfiguration(&mConfig);
}

该方法主要工作:

  • 创建属于自己线程的Looper对象;
  • 超时参数来自于IMS,参数默认值keyRepeatTimeout = 500,keyRepeatDelay = 50

1.2 InputManager.start

[InputManager.cpp]

status_t InputManager::start() {
    result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    ...
    return OK;
}

该方法的主要功能是启动两个线程:

  • 启动线程“InputReader”
  • 启动线程”InputDispatcher“

1.3 input.h

记录两个Android中使用过的类:

KeyEvent

class KeyEvent : public InputEvent {
    ...
    protected:
        int32_t mAction;
        int32_t mFlags;
        int32_t mKeyCode;
        int32_t mScanCode;
        int32_t mMetaState;
        int32_t mRepeatCount;
        nsecs_t mDownTime; //专指按下时间
        nsecs_t mEventTime; //事件发生时间(包括down/up等事件)
}

MotionEvent

class MotionEvent : public InputEvent {
    ...
    protected:
        int32_t mAction;
        int32_t mActionButton;
        int32_t mFlags;
        int32_t mEdgeFlags;
        int32_t mMetaState;
        int32_t mButtonState;
        float mXOffset;
        float mYOffset;
        float mXPrecision;
        float mYPrecision;
        nsecs_t mDownTime; //按下时间
        Vector<PointerProperties> mPointerProperties;
        Vector<nsecs_t> mSampleEventTimes;
        Vector<PointerCoords> mSamplePointerCoords;
    };
}

2. InputReader线程

input设备类型有很多种,以上代码只列举部分常见的设备以及相应的InputMapper:

  • 键盘类设备:KeyboardInputMapper
  • 触摸屏设备:MultiTouchInputMapper或SingleTouchInputMapper
  • 鼠标类设备:CursorInputMapper

InputReader整个过程涉及多次事件封装转换,其主要工作核心是以下三大步骤:

  • getEvents:通过EventHub(监听目录/dev/input)读取事件放入mEventBuffer,而mEventBuffer是一个大小为256的数组, 再将事件input_event转换为RawEvent;
  • processEventsLocked: 对事件进行加工, 转换RawEvent -> NotifyKeyArgs(NotifyArgs) [见小节3.1]
  • QueuedListener->flush:将事件发送到InputDispatcher线程, 转换NotifyKeyArgs -> KeyEntry(EventEntry)

InputReader线程不断循环地执行InputReader.loopOnce(), 每次处理完生成的是EventEntry(比如KeyEntry, MotionEntry), 接下来的工作就交给InputDispatcher线程

3. InputDispatcher线程

ANR超时时间点为 mInputTargetWaitTimeoutTime,该值等于currentTime + 5s, 这里的currentTime是指执行dispatchOnceInnerLocked方法体的起点。此处设置mInputTargetWaitCause等于INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY(应用没有准备就绪),而前面resetANRTimeoutsLocked()过程是唯一用于重置等待理由的地方。

可见,ANR时间区间是从dispatchOnceInnerLocked方法体的起点,直到下次执行handleTargetsNotReadyLocked()方法的这段应用未准备就绪的时间段,该时间段是否超过5s来决定是否触发ANR。

当前这次的事件dispatch过程中执行 findFocusedWindowTargetsLocked() 方法到下一次执行 resetANRTimeoutsLocked() 的时间区间

4. UI线程

创建socket pair,分别位于”InputDispatcher”线程和focused窗口所在进程的UI主线程,可相互通信。

  • UI主线程:通过setFdEvents(), 监听socket客户端,收到消息后回调NativeInputEventReceiver();
  • “InputDispatcher”线程: 通过IMS.registerInputChannel(),监听socket服务端,收到消息后回调handleReceiveCallback;

5. 事件处理全过程

[->android_view_InputEventReceiver.cpp]

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch){
    ...
    if (inputEventObj) {
                //执行Java层的InputEventReceiver.dispachInputEvent【见小节3.3.3】
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                if (env->ExceptionCheck()) {
                    skipCallbacks = true; //分发过程发生异常
                }
                env->DeleteLocalRef(inputEventObj);
            } else {
                skipCallbacks = true;
            }
}

[->InputEventReceiver.java]

private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event); //[见小节3.3.4]
}

[->ViewRootImp.java::WindowInputEventReceiver]

final class WindowInputEventReceiver extends InputEventReceiver {
    public void onInputEvent(InputEvent event) {
       enqueueInputEvent(event, this, 0, true); //【见小节3.3.5】
    }
    ...
}

[->ViewRootImpl.java]

-> enqueInputEvent()
	-> doProcessInputEvents()
    -> deliverInputEvent()
    -> finishInputEvent()

经过一系列的InputStage调用, 最终会分发到真正需要处理该时间的窗口. 当处理完后会调用finishInputEvent()

5.1 交互过程

用一张图来总结交互过程,主要是通过一对socket方式来通信。 当input时间分发到app端, 那么便进入来了InputEventReceiver.dispatchInputEvent()过程.
input_socket

6. ANR原理分析

6.1 ANR处理流程

ANR时间区间便是指当前这次的事件dispatch过程中执行findFocusedWindowTargetsLocked()方法到下一次执行resetANRTimeoutsLocked()的时间区间. 以下5个时机会reset. 都位于InputDispatcher.cpp文件:

  • resetAndDropEverythingLocked
  • releasePendingEventLocked
  • setFocusedApplication
  • dispatchOnceInnerLocked
  • setInputDispatchMode

简单来说, 主要是以下4个场景,会有机会执行resetANRTimeoutsLocked:

  • 解冻屏幕, 系统开/关机的时刻点 (thawInputDispatchingLw, setEventDispatchingLw)
  • wms聚焦app的改变 (WMS.setFocusedApp, WMS.removeAppToken)
  • 设置input filter的过程 (IMS.setInputFilter)
  • 再次分发事件的过程(dispatchOnceInnerLocked)

InputDispatcher线程 findFocusedWindowTargetsLocked()过程调用到handleTargetsNotReadyLocked,且满足超时5s的情况则会调用onANRLocked()

// /frameworks/native/libs/input/android/os/IInputConstants.aidl
/** @hide */
  interface IInputConstants
  {
       const int UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值