您当前的位置: 首页 > 
  • 4浏览

    0关注

    417博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

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

江南才尽,年少无知! 发布时间:2019-03-13 11:42:14 ,浏览量:4

从之前小节我们知道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(this),inputChannel, mMessageQueue);

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

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {
	sp 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 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& 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& 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 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

下一小节再继续详细分析

关注
打赏
1592542134
查看更多评论
立即登录/注册

微信扫码登录

0.0395s