Android按键事件传递流程(一)

1,115 阅读2分钟

「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

Framework层传递

InputManagerService是输入系统的起点,所有相关类和线程因此被创建或间接创建。

Android系统启动后,系统进程SystemServer.java会启动系统的各个服务。

/**
     * Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized.
     */
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    
    //创建null对象
    InputManagerService inputManager = null;
    WindowManagerService wm = null;
    
    //实例化InputManagerService和WindowManagerService
    inputManager = new InputManagerService(context);
    //将InputManagerService对象传给wm
   wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
    
    //将服务添加到ServiceManager管理。
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                              DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                              /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
    
    t.traceBegin("StartInputManager");
    //启动服务
    inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
    inputManager.start();
    t.traceEnd();
}

这里就启动了这个服务。

获取输入事件;对事件的处理、存储;分发、传递给应用层;创建注册服务端管道和应用程序客户端管道;应用层接受从fw层接受的按键事件。

我在这里从Java层开始讲解。

传递给应用层

/frameworks/base/core/java/android/view/ViewRootImpl.java

在native层经过一系列处理后,如果所有的事件读取成功后,会根据时间的类型选择相应的执行语句,最终将事件传递给Java层KeyEvent对象。

最终会由WindowInputEventReceiver进行接收,他继承了InputEventReceiver

 // Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event);
}

WindowInputEventReceiver中重写了onInputEvent这个方法。最终执行用下面方法,下边直接在源码中进行讲解。

enqueueInputEvent(
    processedEvents.get(i), this,
    QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
​
@UnsupportedAppUsage
void enqueueInputEvent(InputEvent event,
                       InputEventReceiver receiver, int flags, boolean processImmediately) {
    //把输入事件封装成QueuedInputEvent对象
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
​
    // Always enqueue the input event in order, regardless of its time stamp.
    // We do this because the application or the IME may inject key events
    // in response to touch events and we want to ensure that the injected keys
    // are processed in the order they were received and we cannot trust that
    // the time stamp of injected events are monotonic.
    
    //将q对象放入QueuedInputEvent对象池中
    //QueuedInputEvent对象中有QueuedInputEvent类型的成员变量mNext
    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    mPendingInputEventCount += 1;
    Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                       mPendingInputEventCount);
​
    if (processImmediately) {
        //从队列中循环取出事件进行处理。
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}

紧接着继续分析doProcessInputEvents()处理流程

doProcessInputEvents()->deliverInputEvent()->deliver(),传递要处理的事件。

public final void deliver(QueuedInputEvent q) {
    //QueuedInputEvent.FLAG_FINISHED标识事件已经被处理
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        forward(q);
    } else if (shouldDropInputEvent(q)) {
        finish(q, false);
    } else {
        traceEvent(q, Trace.TRACE_TAG_VIEW);
        final int result;
        try {
            //正常情况下不会丢弃当前事件,因此会走这里。
            result = onProcess(q);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        apply(q, result);
    }
}

InputStage是抽象基类,这里调用的是ViewPostImeInputStage(表示发送给视图)的onProcess()。在setView()方法中创建了很多InputStage对象,用于处理不同阶段的输入事件,详细请参考源码。

if (q.mEvent instanceof KeyEvent) {
    return processKeyEvent(q);
}

processKeyEvent()将按键事件传给了view的跟DecorView做处理,此处开始,将事件交由应用层去处理,下一节继续讲解。