Android4.4 Input模块笔记

在InputReader从EventHub中获取输入事件,包含触摸屏事件、物理按键事件等,然后转交给InputDispatcher线程,InputDispatcher经过筛选,过滤输入事件。对于触摸事件通过调用findTouchedWindowTargetsLocked()函数找到合适的InputTarget,然后通过dispatchEventLocked()->preparedispatchCycleLocked()->enqueueDispatchEntriesLocked()->enqueueDispatchEntryLocked()->  connection->outboundQueue.enqueueAtTail(dispatchEntry)添加到与InputTarget一一对应的connection中的一个队列中。如果之前该队列无数据,并且当前触摸事件已成功加入该队列,则继续调用startDispatchCycleLocked()函数进行分发处理。在startDispatchCycleLocked()中,有一个while循环,该循环从connection->outboundQueue队列中取出输入事件,如果该输入事件是按键(key)事件,则调用connection->inputPublisher.publishKeyEvent()函数,如果是触摸事件则调用connection->inputPublisher.publishMotionEvent()。publishKeyEvent()和publishMotionEvent()都是调用mChannel->sendMessage()将输入事件发送出去。mChannel是一个C++层InputChannel对象,该对象的赋值过程如下:registerInputChannel()->new Connection->Connection()构造函数->InputPublisher()构造函数。事实上,在registerInputChannel()被调用之前,ViewRootImple在增加一个窗口时调用ViewRootImpl.setView()->mWindowSession.addToDisplay()-WindowManagerService.addWindow(),在addWindow()中会创建一对InputChannel(Nativie层),实际上是创建一对Socket,服务端InputChanel被WMS注册到InputDispatcher中,客户端InputChannel被返回给ViewRootImpl,ViewRootImpl将客户端InputChannel作为参数new一个InputEventReceiver对象,在InputEventReceiver()构造函数中继续调用nativeInit()函数来创建一个native层的NativeInputEventReceiver对象,前面创建的客户端InputChannel会保存在该对象中。

从策划到设计制作,每一步都追求做到细腻,制作可持续发展的企业网站。为客户提供成都网站设计、成都网站制作、网站策划、网页设计、域名申请、网络空间、网络营销、VI设计、 网站改版、漏洞修补等服务。为客户提供更好的一站式互联网解决方案,以客户的口碑塑造优易品牌,携手广大客户,共同发展进步。

总结:WMS会调用native层接口创建一对套接字,服务端保存在InputDispatcher中,客户端保存在NativeInputEventReceiver中(android_view_inputEventReceiver.cpp)。

很容易想到输入事件是从InputDispatcher流向NativeInputEventReceiver中。在创建一个native层的NativeInputEventReceiver对象后会立即调用NativeInputEventReceiver->initialize(),该函数调用mMessageQueue->getLooper()->addFd(fd,0, events, this, NULL)将客户端socket句柄添加到Looper的轮询队列中,参数this指向NativeInputEventReceiver本身,意味着只要服务端InputDispatcher发送输入事件,客户端收到这个事件,就调用NativeInputEventReceiver的某个函数,具体调用哪个函数,自然是NativeInputEventReceiver实现了LooperCallback的接口函数handleEvent()。但此时收到的事件只是代表socket客户端有事件来,并没有把具体的事件读取出来,这点需要注意。

总结:客户端收到输入事件,即调用NativeInputEventReceiver->handleEvent()函数。

在handleEvent()函数中,继续调用consumeEvents()->mInputConsumer.consume()->mChannel->receiveMessage(&mMsg)将具体输入事件读取出来,然后调用env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent,seq, inputEventObj),可以知道native层读取输入事件后,然后会回调java层InputEventReceiver.java中的dispatchInputEvent()函数。事实上,

dispatchInputEvent继续调用onInputEvent(event); 此时可能并不调用InputEventReceiver类中的onInputEvent()方法,而是调用子类onInputEvent()方法。在 ViewRootImpl中存在WindowInputEventReceiver类型变量 mInputEventReceiver,WindowInputEventReceiver类继承InputEventReceiver,并实现 onInputEvent()方法由此可得出结论:native层socket客户端读取输入事件,最终调用InputEventReceiver类子类 的onInputEvent()方法,ViewRootImpl继承InputEventReceiver,因此 ViewRootImpl.onInputEvent()将被调用。

总结:对于一般的触摸屏事件最终处理者是ViewRootImpl类,对于输入法则处理者是IInputMethodSessionWrapper类,当然WMS是不会处理这些输入事件的。

继 续研究ViewRootImpl.onInputEvent()函 数,onInputEvent()->doProcessInputEvents()->deliverInputEvent(),deliverInputEvent() 函数中会调用stage.deliver(q),stage是mFirstPostImeInputStage 或 mFirstInputStage,这个两个InputStage对象在setView中赋值。InputStage类设计就是责任链模式。因为触摸事件是要分发到具体的View上来,所以对于一般的触摸事件最后是传递到ViewPostImeInputStage类中来处理,处理函数是processPointerEvent(q),这个函数调用mView.dispatchPointerEvent(event)将事件分发出去,mView具体是什么呢?mView其实就是DecorView,每一个窗口有且仅有一个DecorView,且处在最顶层,由于DecorView未重写dispatchPointerEvent(),所以调用还是父类View类的dispatchPointerEvent()方法。

[java]view plaincopy

  1. public final boolean dispatchPointerEvent(MotionEvent event) {  

  2.         if (event.isTouchEvent()) {  

  3.             return dispatchTouchEvent(event);  

  4.         } else {  

  5.             return dispatchGenericMotionEvent(event);  

  6.         }  

  7.     }  

该方法继续调用dispatchTouchEvent(event),DecorView重新了该方法:

[java]view plaincopy

  1. @Override  

  2.         public boolean dispatchTouchEvent(MotionEvent ev) {  

  3.             final Callback cb = getCallback();  

  4.             return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)  

  5.                     : super.dispatchTouchEvent(ev);  

  6.         }  

getCallback() 函数获取apk注册的用于拦截按键、触摸等事件的回调函数。一般window不会拦截处理触摸事件,所以会继续调用 super.dispatchTouchEvent(ev),即父类ViewGroup的dispatchTouchEvent()函数,在该函数中寻找 到对应的View再继续调用dispatchTransformedTouchEvent()

[java]view plaincopy

  1. for (int i = childrenCount - 1; i >= 0; i--) {  

  2.                           final int childIndex = customOrder ?  

  3.                                   getChildDrawingOrder(childrenCount, i) : i;  

  4.                           final View child = children[childIndex];  

  5.                           if (!canViewReceivePointerEvents(child)  

  6.                                   || !isTransformedTouchPointInView(x, y, child, null)) {  

  7.                               continue;  

  8.                           }  

  9.   

  10.                           newTouchTarget = getTouchTarget(child);  

  11.                           if (newTouchTarget != null) {  

  12.                               // Child is already receiving touch within its bounds.  

  13.                               // Give it the new pointer in addition to the ones it is handling.  

  14.                               newTouchTarget.pointerIdBits |= idBitsToAssign;  

  15.                               break;  

  16.                           }  

  17.   

  18.                           resetCancelNextUpFlag(child);  

  19.                           if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {  

  20.                               // Child wants to receive touch within its bounds.  

  21.                               mLastTouchDownTime = ev.getDownTime();  

  22.                               mLastTouchDownIndex = childIndex;  

  23.                               mLastTouchDownX = ev.getX();  

  24.                               mLastTouchDownY = ev.getY();  

  25.                               newTouchTarget = addTouchTarget(child, idBitsToAssign);  

  26.                               alreadyDispatchedToNewTouchTarget = true;  

  27.                               break;  

  28.                           }  

  29.                        

具体的分发规则可自行研究代码。

ViewGroup.dispatchTouchEvent()函数分析

[html]view plaincopy

  1. @Override  

  2. public boolean dispatchTouchEvent(MotionEvent ev) {  

  3.     if (mInputEventConsistencyVerifier != null) {  

  4.         mInputEventConsistencyVerifier.onTouchEvent(ev, 1);  

  5.     }  

  6.   

  7.     if (DBG_MOTION || DBG_TOUCH) {  

  8.         Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent 1: ev = " + ev + ",mFirstTouchTarget = "  

  9.                 + mFirstTouchTarget + ",this = " + this);  

  10.     }  

  11.   

  12.     boolean handled = false;  

  13.     if (onFilterTouchEventForSecurity(ev)) {  

  14.         final int action = ev.getAction();  

  15.         final int actionMasked = action & MotionEvent.ACTION_MASK;  

  16.   

  17.         // Handle an initial down.  

  18.         if (actionMasked == MotionEvent.ACTION_DOWN) {  

  19.             // Throw away all previous state when starting a new touch gesture.  

  20.             // The framework may have dropped the up or cancel event for the previous gesture  

  21.             // due to an app switch, ANR, or some other state change.  

  22.             cancelAndClearTouchTargets(ev);  

  23.             resetTouchState();  

  24.         }  

  25.   

  26.         // Check for interception.  

  27.         final boolean intercepted;  

  28.         if (actionMasked == MotionEvent.ACTION_DOWN  

  29.                 || mFirstTouchTarget != null) {  

  30.             final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  

  31.             if (!disallowIntercept) {  

  32.                 intercepted = onInterceptTouchEvent(ev);  

  33.                 /// M : add log to help debugging  

  34.                 if (intercepted == true) {  

  35.                     if (DBG_TOUCH) {  

  36.                         Xlog.d(TAG, "Touch event was intercepted event = " + ev + ",this = " + this);  

  37.                     }  

  38.                 }  

  39.                 ev.setAction(action); // restore action in case it was changed  

  40.             } else {  

  41.                 intercepted = false;  

  42.             }  

  43.         } else {  

  44.             // There are no touch targets and this action is not an initial down  

  45.             // so this view group continues to intercept touches.  

  46.             intercepted = true;  

  47.         }  

  48.   

  49.         // Check for cancelation.  

  50.         final boolean canceled = resetCancelNextUpFlag(this)  

  51.                 || actionMasked == MotionEvent.ACTION_CANCEL;  

  52.   

  53.         // Update list of touch targets for pointer down, if needed.  

  54.         final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;  

  55.         if (DBG_MOTION) {  

  56.             Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent 2: actionMasked = " + actionMasked  

  57.                     + ",intercepted = " + intercepted + ",canceled = " + canceled + ",split = "  

  58.                     + split + ",mChildrenCount = " + mChildrenCount + ",mFirstTouchTarget = "  

  59.                     + mFirstTouchTarget + ",this = " + this);  

  60.         }  

  61.   

  62.         TouchTarget newTouchTarget = null;  

  63.         boolean alreadyDispatchedToNewTouchTarget = false;  

  64.         if (!canceled && !intercepted) {  

  65.             if (actionMasked == MotionEvent.ACTION_DOWN  

  66.                     || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)  

  67.                     || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {  

  68.                 final int actionIndex = ev.getActionIndex(); // always 0 for down  

  69.                 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)  

  70.                         : TouchTarget.ALL_POINTER_IDS;  

  71.   

  72.                 // Clean up earlier touch targets for this pointer id in case they  

  73.                 // have become out of sync.  

  74.                 removePointersFromTouchTargets(idBitsToAssign);  

  75.   

  76.                 final int childrenCount = mChildrenCount;  

  77.                 if (newTouchTarget == null && childrenCount != 0) {  

  78.                     final float x = ev.getX(actionIndex);  

  79.                     final float y = ev.getY(actionIndex);  

  80.                     // Find a child that can receive the event.  

  81.                     // Scan children from front to back.  

  82.                     final View[] children = mChildren;  

  83.   

  84.                     final boolean customOrder = isChildrenDrawingOrderEnabled();  

  85.                     for (int i = childrenCount - 1; i >= 0; i--) {  

  86.                         final int childIndex = customOrder ?  

  87.                                 getChildDrawingOrder(childrenCount, i) : i;  

  88.                         final View child = children[childIndex];  

  89.                         if (!canViewReceivePointerEvents(child)  

  90.                                 || !isTransformedTouchPointInView(x, y, child, null)) {  

  91.                             if (DBG_MOTION) {  

  92.                                 Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent continue 6: i = "  

  93.                                         + i + ",count = " + childrenCount + ",child = " + child  

  94.                                         + ",this = " + this);  

  95.                             }  

  96.                             continue;  

  97.                         }  

  98.   

  99.                         newTouchTarget = getTouchTarget(child);  

  100.                         if (DBG_MOTION) {  

  101.                             Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent to child 3: child = "  

  102.                                     + child + ",childrenCount = " + childrenCount + ",i = " + i  

  103.                                     + ",newTouchTarget = " + newTouchTarget + ",idBitsToAssign = "   

  104.                                     + idBitsToAssign + ",mFirstTouchTarget = " + mFirstTouchTarget   

  105.                                     + ",this = " + this);  

  106.                         }  

  107.                         if (newTouchTarget != null) {  

  108.                             // Child is already receiving touch within its bounds.  

  109.                             // Give it the new pointer in addition to the ones it is handling.  

  110.                             newTouchTarget.pointerIdBits |= idBitsToAssign;  

  111.                             break;  

  112.                         }  

  113.   

  114.                         resetCancelNextUpFlag(child);  

  115.                         if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {  

  116.                             // Child wants to receive touch within its bounds.  

  117.                             mLastTouchDownTime = ev.getDownTime();  

  118.                             mLastTouchDownIndex = childIndex;  

  119.                             mLastTouchDownX = ev.getX();  

  120.                             mLastTouchDownY = ev.getY();  

  121.                             newTouchTarget = addTouchTarget(child, idBitsToAssign);  

  122.                             alreadyDispatchedToNewTouchTarget = true;  

  123.                             break;  

  124.                         }  

  125.                     }  

  126.                 }  

  127.   

  128.                 if (newTouchTarget == null && mFirstTouchTarget != null) {  

  129.                     // Did not find a child to receive the event.  

  130.                     // Assign the pointer to the least recently added target.  

  131.                     newTouchTarget = mFirstTouchTarget;  

  132.                     while (newTouchTarget.next != null) {  

  133.                         newTouchTarget = newTouchTarget.next;  

  134.                     }  

  135.                     newTouchTarget.pointerIdBits |= idBitsToAssign;  

  136.                 }  

  137.             }  

  138.         }  

  139.   

  140.         // Dispatch to touch targets.  

  141.         if (mFirstTouchTarget == null) {  

  142.             // No touch targets so treat this as an ordinary view.  

  143.             handled = dispatchTransformedTouchEvent(ev, canceled, null,  

  144.                     TouchTarget.ALL_POINTER_IDS);  

  145.         } else {  

  146.             // Dispatch to touch targets, excluding the new touch target if we already  

  147.             // dispatched to it.  Cancel touch targets if necessary.  

  148.             TouchTarget predecessor = null;  

  149.             TouchTarget target = mFirstTouchTarget;  

  150.             while (target != null) {  

  151.                 final TouchTarget next = target.next;  

  152.                 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {  

  153.                     handled = true;  

  154.                 } else {  

  155.                     final boolean cancelChild = resetCancelNextUpFlag(target.child)  

  156.                             || intercepted;  

  157.                     if (dispatchTransformedTouchEvent(ev, cancelChild,  

  158.                             target.child, target.pointerIdBits)) {  

  159.                         handled = true;  

  160.                     }  

  161.                     if (DBG_MOTION) {  

  162.                         Xlog.d(TAG, "dispatchTouchEvent middle 5: cancelChild = " + cancelChild  

  163.                                 + ",mFirstTouchTarget = " + mFirstTouchTarget + ",target = "  

  164.                                 + target + ",predecessor = " + predecessor + ",next = " + next  

  165.                                 + ",this = " + this);  

  166.                     }  

  167.                     if (cancelChild) {  

  168.                         if (predecessor == null) {  

  169.                             mFirstTouchTarget = next;  

  170.                         } else {  

  171.                             predecessor.next = next;  

  172.                         }  

  173.                         target.recycle();  

  174.                         target = next;  

  175.                         continue;  

  176.                     }  

  177.                 }  

  178.                 predecessor = target;  

  179.                 target = next;  

  180.             }  

  181.         }  

  182.   

  183.         // Update list of touch targets for pointer up or cancel, if needed.  

  184.         if (canceled  

  185.                 || actionMasked == MotionEvent.ACTION_UP  

  186.                 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {  

  187.             resetTouchState();  

  188.         } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {  

  189.             final int actionIndex = ev.getActionIndex();  

  190.             final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);  

  191.             removePointersFromTouchTargets(idBitsToRemove);  

  192.         }  

  193.     }  

  194.   

  195.     if (DBG_MOTION) {  

  196.         Xlog.d(TAG, "(ViewGroup)dispatchTouchEvent end 4: handled = " + handled + ",mFirstTouchTarget = "  

  197.                 + mFirstTouchTarget + ",this = " + this);  

  198.     }  

  199.   

  200.     if (!handled && mInputEventConsistencyVerifier != null) {  

  201.         mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);  

  202.     }  

  203.     return handled;  


文章题目:Android4.4 Input模块笔记
文章路径:http://scyanting.com/article/gssgsd.html