android framework层input事件接收和派发

    技术2025-04-26  23

    input序列图

     前面是启动activity时,view的添加过程。在view添加过程中会注册inputchannel,而inputchannel是input事件处理的核心通道。在本篇里所有的介绍都是围绕inputchannel的创建和读取展开的,前面的view添加是窗口管理部分不是本篇关注的内容就不介绍了。本篇从inputchannel的创建开始介绍。

     

    1.openInputChannel

    在Windowstate.java文件里面,来看看这个代码实现

    从上面的截图可以看出,这个函数主要做了两件事。

    1.调用接口生成能够相互读写的socket

    2.将其中的一个socket注册到inputflinger HAL层

    2.openInputChannelPair

    这个函数主要是调用native的JNI方法,生成socket并返回。

    public static InputChannel[] openInputChannelPair(String name) { if (name == null) { throw new IllegalArgumentException("name must not be null"); } if (DEBUG) { Slog.d(TAG, "Opening input channel pair '" + name + "'"); } return nativeOpenInputChannelPair(name); }

    其JNI的实现在,android_view_InputChannel.cpp

    static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env, jclass clazz, jstring nameObj) { const char* nameChars = env->GetStringUTFChars(nameObj, NULL); String8 name(nameChars); env->ReleaseStringUTFChars(nameObj, nameChars); sp<InputChannel> serverChannel; sp<InputChannel> clientChannel; status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); if (result) { String8 message; message.appendFormat("Could not open input channel pair. status=%d", result); jniThrowRuntimeException(env, message.string()); return NULL; } jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL); if (env->ExceptionCheck()) { return NULL; } jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, std::make_unique<NativeInputChannel>(serverChannel)); if (env->ExceptionCheck()) { return NULL; } jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, std::make_unique<NativeInputChannel>(clientChannel)); if (env->ExceptionCheck()) { return NULL; } env->SetObjectArrayElement(channelPair, 0, serverChannelObj); env->SetObjectArrayElement(channelPair, 1, clientChannelObj); return channelPair; }

    其核心就是调用InputTransport.cpp 源文件的openInputChannelPair来最终生成socket,实现截图如下:

    3-4 registerInputChannel

    在上两步中,生成了一对能相互读写的socket,接下来就是将serversocket注册到inputflinger中,从而inputflinger向这个socket写事件,而HAL则在clientsocket中读事件,然后通知framework.其JNI层向inputflinger注册channel的代码如下:

    static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); if (inputChannel == NULL) { throwInputChannelNotInitialized(env); return; } sp<InputWindowHandle> inputWindowHandle = android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj); status_t status = im->registerInputChannel( env, inputChannel, inputWindowHandle, monitor); if (status) { String8 message; message.appendFormat("Failed to register input channel. status=%d", status); jniThrowRuntimeException(env, message.string()); return; } if (! monitor) { android_view_InputChannel_setDisposeCallback(env, inputChannelObj, handleInputChannelDisposed, im); }

    最终是调inputDispatch.cpp文件中的registerInputChannel接口,将这个socket注册进去

    5.WindowInputEventReceiver

    这个类定义在ViewRootImpl.java文件里面,它是InputEventReceiver类的最终子类,是JAVA层Input事件处理的起始和数据的最初来源

    首先来看下InputEventReceiver的构造函数:

    public InputEventReceiver(InputChannel inputChannel, Looper looper) { if (inputChannel == null) { throw new IllegalArgumentException("inputChannel must not be null"); } if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mInputChannel = inputChannel; mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this), inputChannel, mMessageQueue); mCloseGuard.open("dispose"); }

    这个构造函数的核心就是调用native方法,把clientsocket和消息队列传递给JNI

    6.nativeInit

    7.NativeInputEventReceiver

    class NativeInputEventReceiver : public LooperCallback

    首先NativeInputEventReceiver是LooperCallback的一个子类,LooperCallback

    关于Looper的机制,可以参考

    https://blog.csdn.net/xiaosayidao/article/details/73992078?ops_request_misc=&request_id=&biz_id=102&utm_term=LooperCallback&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-3-73992078

    这篇文章,这里就不仔细说了。Looper类处理事件的入口函数为handleEvent

    可以根据序列图的调用顺序跟踪代码流程。

    8.dispatchInputEvent

    这个函数是JNI反调的,是JAVA层input数据的入口和来源,来看看这个函数的实现

    @SuppressWarnings("unused") private void dispatchInputEvent(int seq, InputEvent event, int displayId) { mSeqMap.put(event.getSequenceNumber(), seq); onInputEvent(event, displayId); }

    如图,这个函数很简单,主要就是调接口onInputEvent处理数据,而WindowInputEventReceiver子类重写了这个接口,看其实现如下:

    @Override public void onInputEvent(InputEvent event, int displayId) { enqueueInputEvent(event, this, 0, true); }

    9.java层input事件派发

    如8的第二幅图所示,数据会通过enqueueInputEvent接口将InputEvent数据放到队列的尾部,然后从队列头部开始处理和派发事件

    具体的doProcessInputEvents处理过程,可以自己跟踪代码了解下。这里需要说明的是deliverInputEvent是inputevent事件派发到View的最后一步调用,上面的序列图上没有体现出来。这里再来幅序列图,看看这个inputevent是如何从ViewRootImpl到View的:

    这里需要注意的是,事件会被InputStage类进行拦截处理。如果本类处理不了,会交由下个InputStage类处理

    这里是各种inputstage的层次关系,它们都是InputStage的子类。通过构造函数,将其赋值给类成员变量next.如果本类无法处理,则会调用next类的deliver函数来处理,来看看deliver的实现:

    public final void deliver(QueuedInputEvent q) { if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { forward(q); } else if (shouldDropInputEvent(q)) { finish(q, false); } else { apply(q, onProcess(q)); } }

    我们View处理key和touch事件是在ViewPostImeInputStage这个stage里面,这里来看看这个类的process对input event的处理 :

    后续可以根据序列图,自行跟踪代码的处理流程。本篇是介绍framework inputevent的处理流和,inputflinger的 inputevent 的处理流程可以参考我上篇对inputflinger的介绍。

    本篇到此完结,如有问题敬请指出。

    Processed: 0.013, SQL: 9