您当前的位置: 首页 > 

06.输入系统:第10课第14节_输入系统_APP获得并处理输入事件流程

发布时间:2019-03-13 11:42:14 ,浏览量:6

从之前小节我们知道Dispatcher线程是如何分发事件给APP的,该小节我们讲解APP如何获取,并且处理这些输入事件。在前面的小节中,提到APP在得到一个fd之后,会把他封装成一个InputChannel,然后再封装成windowInputEvenReceiver,最终把fd注册到Looper中,使用epoll进行查询等待,该小节我们详细的分析fd注册到Looper的过程,APP调用总的详细框图如下: 在这里插入图片描述

fd事件注册

在应用程序中,对于文件句柄fd,InputChannel的注册是从框图中ViewRootImpl.java文件,的setView方法中的WindowInputEventReceiver开始,其ViewRootImpl.java文件的调用过程如下:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { /*从调用.addToDisplay方法获取mInputChannel*/ res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mInputChannel); /*注册fd,或者说InputChannel,因为fd被包含在InputChannel中*/ mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper(); 

现在我们从WindowInputEventReceiver(mInputChannel,Looper.myLooper()开始分析,先查看 WindowInputEventReceiver这个类包含了什么

final class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } @Override public void onInputEvent(InputEvent event) { enqueueInputEvent(event, this, 0, true); } @Override public void onBatchedInputEventPending() { if (mUnbufferedInputDispatch) { super.onBatchedInputEventPending(); } else { scheduleConsumeBatchedInput(); } } @Override public void dispose() { unscheduleConsumeBatchedInput(); super.dispose(); } } 

从上面我们可以看到,WindowInputEventReceiver继承于InputEventReceiver,然后重写了onInputEvent(InputEvent event),onBatchedInputEventPending(),dispose()三个方法。当接收到事件的时候,onInputEvent方法会被调用。

既然WindowInputEventReceiver继承于InputEventReceiver,那么当创建WindowInputEventReceiver实例化对象的时候,InputEventReceiver的构造方法肯定会被调用。我们可以看到InputEventReceiver中包含如下函数:

private void dispatchInputEvent(int seq, InputEvent event) { onInputEvent(event); 

从上分析,子类WindowInputEventReceiver中的onInputEvent方法,应该是被父类InputEventReceiver的dispatchInputEvent方法调用的,

再来看看InputEventReceiver的构造函数,

public InputEventReceiver(InputChannel inputChannel, Looper looper) { mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),inputChannel, mMessageQueue); 

该处的nativeInit会调用android_view_InputEventReceiver.cpp中的:

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) { sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue); 

这样就从java到cpp文件了,在android_view_InputEventReceiver.cpp文件中,存在函数:

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) { /*最终调用java中的dispatchInputEvent方法*/ env->CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchBatchedInputEventPending); 

我们再次回到android_view_InputEventReceiver.cpp的 nativeInit函数:

static jlong nativeInit(JNIEnv* env, jclass clazz, jobjectreceiverWeak,jobject inputChannelObj, jobject messageQueueObj) { /*获取一个NativeInputEventReceiver类型的实例化对象*/ sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue); /*通过其实例化对象,调用initialize()函数*/ status_t status = receiver->initialize(); /*该处就是把InputChannel的fd注册到Looper*/ setFdEvents(ALOOPER_EVENT_INPUT); /*InputChannel从获取fd*/ int fd = mInputConsumer.getChannel()->getFd(); /*添加到Looper,注意该处的第3个参数是this,表示当前这个实例化对象,即
	 			receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);	*/ mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL); 

现在我们打开SDK/system/core/libutils/looper.cpp文件,在其类可以找打两个Looper::addFd函数:

int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) { int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) { 

根据两个函数倒数第二个参数,前面的为一个函数指针,第二个为一个实LooperCallback例化对象,但是我们之前通过receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue)并非LooperCallback例化对象,那么可以猜测到NativeInputEventReceiver为LooperCallback的一个派生类。

在android_view_InputEventReceiver.cpp可以看到如下:

class NativeInputEventReceiver : public LooperCallback { 

这样就证明了我们的猜测,那么他调用的就是第二个int Looper::addFd,

int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) { /*构建一个Reques*/ Request request; request.fd = fd; /*调用该函数,添加到epoll_ctl之中*/ int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); 

这样,就可以使用epoll,对输入事件进行监测了。 我们来看看LooperCallback包含呢那些东西:

virtual int handleEvent(int fd, int events, void* data) = 0 

该 handleEvent会调用派生类中的consumeEvents,这样注册部分就讲解完成,下面我们来看看APP获取事件的流程。

获取事件

再次打开Looper.cpp,在其了存在:

int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) { result = pollOnce(timeoutMillis, outFd, outEvents, outData); result = pollInner(timeoutMillis); /*等待fd动作*/ eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); /*根据返回结果,取出若干个事件*/ for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; /*通过fd取出在注册时构建的requestIndex*/ ssize_t requestIndex = mRequests.indexOfKey(fd); /*存放在某个缓存中*/ pushResponse(events, mRequests.valueAt(requestIndex)); /*从缓存逐个取出esponse,逐个处理*/ for (size_t i = 0; i < mResponses.size(); i++) { /*Response中包含呢request*/ Response& response = mResponses.editItemAt(i); /*此处的callback指向的是之前的receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);
        		该处的andleEvent,即为LooperCallback中的virtual int handleEvent(int fd, int events, void* data) = 0*/ int callbackResult = response.request.callback->handleEvent(fd, events, data); 

我们查看android_view_InputEventReceiver.cpp中实现的handleEvent:

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL); /*调用InputEventReceiver.java中的dispatchInputEvent*/ env->CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchBatchedInputEventPending); 

然后在InputEventReceiver.java中的dispatchInputEvent:

private void dispatchInputEvent(int seq, InputEvent event) { /*该具体方法,为派生类实现的*/ onInputEvent(event); 

现在我们来查看ViewRootImpl.java中实现的onInputEvent:

public void onInputEvent(InputEvent event) { enqueueInputEvent(event, this, 0, true);//放入队列 void enqueueInputEvent(InputEvent event,InputEventReceiver receiver, int flags, boolean processImmediately) { doProcessInputEvents();//处理众多的事件 deliverInputEvent(q);//传输事件 /*把事件分为多个阶段*/ InputStage stage; 

其上事件的阶段处理详细阶梯图如下: 在这里插入图片描述 现在我们对这个阶梯图进行讲解,在最开始提到WindowInputEventReceiver是java层面对输入事件处理的总入口,他会调用onInputEvent,在根据之前分析从onInputEvent到deliverInputEvent,deliverInputEvent会把输入事件分为多个阶段,

1.NativePrelme与ViewPrelme为输入法之前,对输入事件的处理。 2.lme对输入法的处理 3.EarlyPostime到viewPostime为输入法之后做的处理 4.Synthetic为对事件综合的处理 如果你的应用程序不使用输入法,他会从3开始处理,如果使用输入法,从1开始处理。

应用程序可能是C++编写,也可能是java编写, c++:使用Native Activity处理 java:使用View-onKeyDown()处理

在框图右边的部分,都处于一个链表,我们看看这个链表是如何创建出来的: 在ViewRootImpl.java可以找:

CharSequence counterSuffix = attrs.getTitle(); mSyntheticInputStage = new SyntheticInputStage(); InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); 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); 

该处我们可以看待先创建Synthetic,然后他当做参数传入ViewPostImeInputStage…依此递推,就创建一个链表出来。

知道链表怎么创建之后,我们再回到deliverInputEvent:

private void deliverInputEvent(QueuedInputEvent q) { if (q.shouldSendToSynthesizer()) { /*判断是否能够忽略输入法,从图示对应位置开始执行*/ stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; stage.deliver(q);//为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));//没有结束,也不能抛弃,这执行该函数 } 

从上可知,每个阶段进行处理的时候,只有3个结果 1.如果已经完成,进入下一阶段 2.设置标志位为finish,传递给下一阶段 3.调用onProcess函数处理,处理完成传递给下一个stage

下一小节再继续详细分析

关注
打赏
1688896170
查看更多评论

暂无认证

  • 6浏览

    0关注

    115984博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文
立即登录/注册

微信扫码登录

0.0738s