本文是综合
《深入理解 Android 卷 III(第五章 深入理解 Android 输入系统)》
《Android 系统源代码情景分析(第 14章 Android 应用程序的键盘消息处理机制)》
《深入解析 Android 5.0 系统(第 16 章 Android 的输入管理)》
三章的总结。

一、输入系统的总体流程

. Linux 内核
接受输入设备的中断,并将原始事件的数据写入设备节点中;

. 设备节点
作为内核月 IMS 的桥梁,将原始事件的数据暴露给用户空间, 以便 IMS 可以从中读取事件;

. EventHub
直接访问所有的设备节点,将所有的输入事件通过 getEvents() 接口把多个输入设备节点中读取的数据交给 InputReader, 它是输入系统最底层的一个组件;

. InputReader
运行独立线程中,负责管理输入设备的列表与配置, 对输入事件进行加工处理,然后交给 InputDispatcher 处理;

. InputDispatcher
运行独立线程中,保存来至 WMS 的窗口信息,将从 InputReader 传过来的输入事件派发到合适的窗口;

. InputReaderPolicy
为 InputReader 的事件加工处理提供一些策略配置;

. InputDispatcherPolicy
为 InputDispatcher 的派发过程提供策略控制,例如截取某些特定的输入事件作为特殊用途,或者阻止将某些事件派发给目标窗口。例如 Home 键事件被截取到 PhoneWindowManager 中处理,并阻止窗口收到 Home 键按下的事件;

. WindowManagerService
当创建窗口时 WMS 为新窗口和 IMS 创建事件传递的通道 InputChannel;

. ViewRootImpl
对某些窗口,例如壁纸, SurfaceView 的窗口,窗口就是事件派发的终点;
对于其他例如 Activity, 对话框等使用了 Android 控件系统的窗口来说,输入终点是 View。 ViewRootImpl 将窗口所接收的输入事件沿着控件树将事件派发给感兴趣的控件。

整体流程:
内核将原始事件写入设备节点中, InputReader 不断地通过 EventHub 将原始事件取出来并翻译加工成 Android 输入事件,然后交给 InputDispatcher 。InputDispatcher 根据 WMS 提供的窗口信息将事件交给合适的窗口。窗口的 ViewRootImpl 对象再沿着控件树将事件派发给刚兴趣的控件。

二、IMS 的结构体系

  1. InputManagerService 在 SystemService 中的 ServerThread 线程中启动;
    SystemServer.java 的 ServerThread.run() 方法中
inputManager = new InputManagerService(context, wmHandler);wm = WindowManagerService.main(context, power, display, inputManager,                    uiHandler, wmHandler,                    factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,                    !firstBoot, onlyCore);ServiceManager.addService(Context.WINDOW_SERVICE, wm);ServiceManager.addService(Context.INPUT_SERVICE, inputManager);inputManager.setWindowManagerCallbacks(wm.getInputMonitor());inputManager.start();
  1. 在 InputManagerService 的构造函数中,调用 nativeInite(…) 方法,在该方法内创建 NativeInputManager
    framework/services/jni/com_android_input_InputManagerService.cpp
    NativeInputManager::NativeInputManager(…)
NativeInputManager::NativeInputManager(jobject contextObj,        jobject serviceObj, const sp& looper) :        mLooper(looper) {    JNIEnv* env = jniEnv();    ...    // 创建 EventHub    sp eventHub = new EventHub();    // 创建 Native 层的 InputManager    mInputManager = new InputManager(eventHub, this, this);}
  1. 在 InputManager 的构造函数中,创建 InputReader 和 InputDispatcher
    framework/services/input/InputManager.cpp
InputManager::InputManager(        const sp& eventHub,        const sp& readerPolicy,        const sp& dispatcherPolicy) {    // 创建 InputDispatcher    mDispatcher = new InputDispatcher(dispatcherPolicy);    // 创建 InputReader    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);    initialize();}void InputManager::initialize() {    // 为 InputReader 创建运行线程 InputeReaderThread    mReaderThread = new InputReaderThread(mReader);    // 为 InputDispatcher 创建运行线程 InputDispatcherThread    mDispatcherThread = new InputDispatcherThread(mDispatcher);}

IMS 创建完成后, 在 ServerThread 中调用 InputManagerService.start() 启动。

  1. 启动后,
    InputReader 在其线程循环中不断从 EventHub 中抽取原始输入事件,进行处理加工,然后将事件方法 InputDispatcher 的派发队列中;

    InputDispatcher 则在其线程循环中将派发队列中的事件取出,查找合适窗口,并将事件写入窗口的事件接收管道中 InputChannel;

    窗口事件接收线程的 Looper 从管道中将事件取出,交给事件处理函数进行事件响应。

  2. EventHub
    EventHub 的 getEvnts() 方法利用 Linux 提供的两套机制 INotify 与 Epoll 解决事件的读取问题。

  3. InputReader 的总体流程
    InputReader 运行在 InputReaderThread 线程中,当线程运行后,会不断的调用 threadLoop() ,直到此函数返回 false, 则退出线程循环,从而结束线程。
    framework/services/input/InputManager.cpp

bool InputReaderThread::threadLoop() {    mReader->loopOnce();    return true;}void InputReader::loopOnce() { ...     // 1. 从 EventHub 中抽取未处理的事件列表;    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);    { // acquire lock        AutoMutex _l(mLock);        ...        if (count) {            // 2. 通过 processEventsLocked() 对事件进行处理, 并将处理后的结果            // 暂存到 mQueueListener 中            processEventsLocked(mEventBuffer, count);        }        ...    } // release lock     ...     // 3. 调用 mQueueListener.flush() 方法将所有暂存的输入事件一次性地交付给      // InputDispatcher     mQueuedListener->flush();}

三、 InputDispatcher 与窗口的通信

过程:
先将输入事件放入 Connection 的 outboundQueue 队列中,然后在由 mInputPublisher 依次将队列中的事件封装为 InputMessage 并写入 Service InputChannel, 直到队列为空或 Service InputChannel 的写入缓冲区满。

写入的事件将被移到 waitQueue 队列里。随后派发线程进入休眠状态。

当窗口在另一端读取事件并发来反馈后,派发线程因 Service InputChannel 可读而被唤醒,并在 handleReceiveCallback() 中通过 Connection 的 mInputPublisher 读取反馈信息,将其与 waitQueue 中等待反馈的事件进行配对成功后,将事件从 waitQueue 中移除,完成事件的派发过程。

  1. InputDispatcher 与 窗口通过 InputChannel 通信;

  2. InputChannel 本质是一对 SocketPair ,而 SocketPair 用来实现在本机内进行进程间的通信, InputChannel 是 SocketPair 描叙符及其操作的封装,而且是成对使用的
    framework/libs/androidfw/InputTransport.cpp

status_t InputChannel::openInputChannelPair(const String8& name,        sp& outServerChannel, sp& outClientChannel) {    int sockets[2];    // 通过 socketpair(...) 创建一对 SocketPair, 并保存在 sockets 数组中    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {       ...        return result;    }    ...    // 创建 Server 端的 InputChannel 对象    outServerChannel = new InputChannel(serverChannelName, sockets[0]);    ...    // 创建 Client 端的 InputChannerl 对象    outClientChannel = new InputChannel(clientChannelName, sockets[1]);    return OK;}

3.将信息封装成 InputMessage 在 两个 InputChannel 之间通信

4.在 WMS添加窗口是,会创建一对 InputChannel, 其中一个保存在 WindowState 中,并注册给 IMS, 这是 server InputChannel.
另外一个则通过传出参数 outChannel 交给调用者,这是 client InputChannel.
framework/services/java/com/android/server/wm/WindowManagerService.java

public int addWindow(Session session, IWindow client, int seq,   WindowManager.LayoutParams attrs, int viewVisibility, int displayId,            Rect outContentInsets, InputChannel outInputChannel) {    // 创建 InputChannels    if (outInputChannel != null && (attrs.inputFeatures                   &WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {           String name = win.makeInputChannelName();         InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);       //  Service 端的 InputChannel       win.setInputChannel(inputChannels[0]);       // Client 端的 InputChannel       inputChannels[1].transferTo(outInputChannel);      // InputManagerService , 将 WindowState 所保存的      // InputChannel 向 InputManagerService 注册 mInputManager.registerInputChannel(win.mInputChannel,win.mInputWindowHandle);    }// 将所有窗口信息更新到 InputManagerService 中 mInputMonitor.updateInputWindowsLw(false /*force*/); }

5.Client 端对事件的接收
当 InputPublisher 将事件以 InputMessage 的形式写入 inputChannel 中之后, 窗口端的 Looper 会因此而被唤醒,并执行 NativeInputEventReceiver 的 handleEvent().
framework/core/jni/android_view_InputEventReceiver.cpp

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,        bool consumeBatches, nsecs_t frameTime) {    ...    ScopedLocalRef receiverObj(env, NULL);    bool skipCallbacks = false;    for (;;) {        uint32_t seq;        InputEvent* inputEvent;        // 1. consume() 从 InputChannel 中读取一条 InputMessage,解析为 InputEvent        status_t status = mInputConsumer.consume(&mInputEventFactory,                consumeBatches, frameTime, &seq, &inputEvent);        ...        if (!skipCallbacks) {        ...         // 2. 根据事件类型创建 Java 层的 KeyEvent 和 MotionEvent            jobject inputEventObj;            switch (inputEvent->getType()) {            case AINPUT_EVENT_TYPE_KEY:                ...                // 创建 KeyEvent 事件                inputEventObj = android_view_KeyEvent_fromNative(env,                        static_cast(inputEvent));                break;            case AINPUT_EVENT_TYPE_MOTION:                ...                // 创建 MotionEvent 事件                inputEventObj = android_view_MotionEvent_obtainAsCopy(env,                        static_cast(inputEvent));                break;            default:               ...            }            if (inputEventObj) {                ...            // 3. 通过 JNI 回调 Java 层的 InputEventReceiver 的             // dispatcheInputEvent() 方法                env->CallVoidMethod(receiverObj.get(),                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);                ...            } else {              ...            }        }       ...    }}

framework/core/java/android/view/InputEventReceiver.java
InputEventReceiver.dispatchInputEvent(…)

 private void dispatchInputEvent(int seq, InputEvent event) { // 以 event 的序列号为键,将来自 InputDispacher 的序列号保存到 Map中。java 层     // 在创建时也会分配一个唯一的序号,用来进行唯一标识。      mSeqMap.put(event.getSequenceNumber(), seq);      onInputEvent(event);    }

至此,我们的事件传递过程终于回到 Java 层了,整个事件的接收部分工作完成了。 onInputEvent() 由使用者重写,从而实现各种各样的工作。

注: 按键事件与通用事件的派发流程相同基本相同
不同点:
1. 按键事件通过 notifykey() 方法进入 InputDispatcher;
2. 按键事件的派发目标通过焦点方式进行查找目标窗口;

四、事件在应用进程的处理

前面我们的 InputEvent 已经到 InputEventReceiver.onInputEvent() 方法,而在 ViewRootImpl.java 中 WindowInputEventReceiver extends InputEventReceiver

final class WindowInputEventReceiver extends InputEventReceiver {        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {            super(inputChannel, looper);        }        @Override        public void onInputEvent(InputEvent event) {            // 调用了 ViewRootImpl.enqueueInputEvent(...) 方法            enqueueInputEvent(event, this, 0, true);        }        @Override        public void onBatchedInputEventPending() {            scheduleConsumeBatchedInput();        }        @Override        public void dispose() {            unscheduleConsumeBatchedInput();            super.dispose();        }    }

ViewRootImpl.enqueueInputEvent(…) 方法最终会调用 InputStage.deliver() 方法

  1. 理解 InputStatge

InputStage 在创建的时候,利用前一个 InputStage 传进构造方法里,形成流水线对消息的处理。

流水线的创建
framewoke/core/java/android/view/ViewRootImpl.java

InputStage syntheticStage = new SyntheticInputStage();InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticStage);InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,                        "aq:native-post-ime:" + counterSuffix);InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);InputStage imeStage = new ImeInputStage(earlyPostImeStage,                        "aq:ime:" + counterSuffix);InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,                        "aq:native-pre-ime:" + counterSuffix);mFirstInputStage = nativePreImeStage;mFirstPostImeInputStage = earlyPostImeStage;
  1. 这里注意看看 ViewPreImeInputStage 和 ViewPostImeStage
    framewoke/core/java/android/view/ViewRootImpl.java

ViewPreImeInputStage

  final class ViewPreImeInputStage extends InputStage {        public ViewPreImeInputStage(InputStage next) {            super(next);        }        @Override        protected int onProcess(QueuedInputEvent q) {            if (q.mEvent instanceof KeyEvent) {                return processKeyEvent(q);            }            return FORWARD;        }        private int processKeyEvent(QueuedInputEvent q) {            final KeyEvent event = (KeyEvent)q.mEvent;            // mView 是 DecorView            if (mView.dispatchKeyEventPreIme(event)) {                return FINISH_HANDLED;            }            return FORWARD;        }    }

processKeyEvent() 方法调用了 mView.dispatcheKeyEventPreIme(…) 方法。这里的 mView 是 DecorView.这将导致 Activity 的控件树中所有的 View 的 onKeyPreIme() 方法被调用。这样就给了 * View 在输入法处理 Key 事件前先得到消息并处理的机会。

ViewPostImeStage

  */    final class ViewPostImeInputStage extends InputStage {        public ViewPostImeInputStage(InputStage next) {            super(next);        }        // 根据不同的事件类型处理事件        @Override        protected int onProcess(QueuedInputEvent q) {            // 按键事件            if (q.mEvent instanceof KeyEvent) {                return processKeyEvent(q);            } else {                final int source = q.mEvent.getSource();                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {                    // 触屏事件                    return processPointerEvent(q);                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {                    // 轨迹球事件                    return processTrackballEvent(q);                } else {                    // 其他事件                    return processGenericMotionEvent(q);                }            }        }        // mView 都是 DecorView        private int processKeyEvent(QueuedInputEvent q) {            final KeyEvent event = (KeyEvent)q.mEvent;            // Deliver the key to the view hierarchy.            if (mView.dispatchKeyEvent(event)) {                return FINISH_HANDLED;            }          }      private int processPointerEvent(QueuedInputEvent q) {            final MotionEvent event = (MotionEvent)q.mEvent;            if (mView.dispatchPointerEvent(event)) {                return FINISH_HANDLED;            }            return FORWARD;        }        private int processTrackballEvent(QueuedInputEvent q) {            final MotionEvent event = (MotionEvent)q.mEvent;            if (mView.dispatchTrackballEvent(event)) {                return FINISH_HANDLED;            }            return FORWARD;        }        private int processGenericMotionEvent(QueuedInputEvent q) {            final MotionEvent event = (MotionEvent)q.mEvent;            // Deliver the event to the view.            if (mView.dispatchGenericMotionEvent(event)) {                return FINISH_HANDLED;            }            return FORWARD;        }

最后事件都是调用 DecorView 中的方法出来各类的输入事件,回调到 Activity 中的回调方法。

更多相关文章

  1. Android之菜单总结
  2. Android使用Retrofit进行网络请求
  3. Android官方入门文档[1]创建一个Android项目
  4. android 创建桌面快捷方式 、插件
  5. AIR Native Extension的使用(Android)一 : 打包ane
  6. 创建android逐帧动画的两种方式
  7. [android]在上下文菜单的选中事件中获取列表选中的元素
  8. 使用sencha cmd创建android应用
  9. android 多点触控

随机推荐

  1. Android开发日记 --(1)Android系统开发环境
  2. Android: Android Resources
  3. Android Studio升级3.2以后 Androidx 异
  4. Android密码明文密文切换
  5. Android启动过程之SystemServer
  6. android百度地图标记点代码
  7. Android原始XML的读写操作
  8. Android _Android登录对话框实现(1)
  9. Android系统启动-SystemServer下篇
  10. Android涉及到的设计模式