Android4.4Input模块笔记-创新互联

在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会保存在该对象中。

成都创新互联公司是一家集网站建设,卓尼企业网站建设,卓尼品牌网站建设,网站定制,卓尼网站建设报价,网络营销,网络优化,卓尼网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。

总结: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.                           newTouchTarget = getTouchTarget(child);

  10.                           if (newTouchTarget != null) {

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

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

  13.                               newTouchTarget.pointerIdBits |= idBitsToAssign;

  14.                               break;

  15.                           }

  16.                           resetCancelNextUpFlag(child);

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

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

  19.                               mLastTouchDownTime = ev.getDownTime();

  20.                               mLastTouchDownIndex = childIndex;

  21.                               mLastTouchDownX = ev.getX();

  22.                               mLastTouchDownY = ev.getY();

  23.                               newTouchTarget = addTouchTarget(child, idBitsToAssign);

  24.                               alreadyDispatchedToNewTouchTarget = true;

  25.                               break;

  26.                           }

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

ViewGroup.dispatchTouchEvent()函数分析

[html] view plaincopy

  1. @Override

  2. public boolean dispatchTouchEvent(MotionEvent ev) {

  3.     if (mInputEventConsistencyVerifier != null) {

  4.         mInputEventConsistencyVerifier.onTouchEvent(ev, 1);

  5.     }

  6.     if (DBG_MOTION || DBG_TOUCH) {

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

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

  9.     }

  10.     boolean handled = false;

  11.     if (onFilterTouchEventForSecurity(ev)) {

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

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

  14.         // Handle an initial down.

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

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

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

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

  19.             cancelAndClearTouchTargets(ev);

  20.             resetTouchState();

  21.         }

  22.         // Check for interception.

  23.         final boolean intercepted;

  24.         if (actionMasked == MotionEvent.ACTION_DOWN

  25.                 || mFirstTouchTarget != null) {

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

  27.             if (!disallowIntercept) {

  28.                 intercepted = onInterceptTouchEvent(ev);

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

  30.                 if (intercepted == true) {

  31.                     if (DBG_TOUCH) {

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

  33.                     }

  34.                 }

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

  36.             } else {

  37.                 intercepted = false;

  38.             }

  39.         } else {

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

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

  42.             intercepted = true;

  43.         }

  44.         // Check for cancelation.

  45.         final boolean canceled = resetCancelNextUpFlag(this)

  46.                 || actionMasked == MotionEvent.ACTION_CANCEL;

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

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

  49.         if (DBG_MOTION) {

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

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

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

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

  54.         }

  55.         TouchTarget newTouchTarget = null;

  56.         boolean alreadyDispatchedToNewTouchTarget = false;

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

  58.             if (actionMasked == MotionEvent.ACTION_DOWN

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

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

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

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

  63.                         : TouchTarget.ALL_POINTER_IDS;

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

  65.                 // have become out of sync.

  66.                 removePointersFromTouchTargets(idBitsToAssign);

  67.                 final int childrenCount = mChildrenCount;

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

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

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

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

  72.                     // Scan children from front to back.

  73.                     final View[] children = mChildren;

  74.                     final boolean customOrder = isChildrenDrawingOrderEnabled();

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

  76.                         final int childIndex = customOrder ?

  77.                                 getChildDrawingOrder(childrenCount, i) : i;

  78.                         final View child = children[childIndex];

  79.                         if (!canViewReceivePointerEvents(child)

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

  81.                             if (DBG_MOTION) {

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

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

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

  85.                             }

  86.                             continue;

  87.                         }

  88.                         newTouchTarget = getTouchTarget(child);

  89.                         if (DBG_MOTION) {

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

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

  92.                                     + ",newTouchTarget = " + newTouchTarget + ",idBitsToAssign = "

  93.                                     + idBitsToAssign + ",mFirstTouchTarget = " + mFirstTouchTarget

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

  95.                         }

  96.                         if (newTouchTarget != null) {

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

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

  99.                             newTouchTarget.pointerIdBits |= idBitsToAssign;

  100.                             break;

  101.                         }

  102.                         resetCancelNextUpFlag(child);

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

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

  105.                             mLastTouchDownTime = ev.getDownTime();

  106.                             mLastTouchDownIndex = childIndex;

  107.                             mLastTouchDownX = ev.getX();

  108.                             mLastTouchDownY = ev.getY();

  109.                             newTouchTarget = addTouchTarget(child, idBitsToAssign);

  110.                             alreadyDispatchedToNewTouchTarget = true;

  111.                             break;

  112.                         }

  113.                     }

  114.                 }

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

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

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

  118.                     newTouchTarget = mFirstTouchTarget;

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

  120.                         newTouchTarget = newTouchTarget.next;

  121.                     }

  122.                     newTouchTarget.pointerIdBits |= idBitsToAssign;

  123.                 }

  124.             }

  125.         }

  126.         // Dispatch to touch targets.

  127.         if (mFirstTouchTarget == null) {

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

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

  130.                     TouchTarget.ALL_POINTER_IDS);

  131.         } else {

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

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

  134.             TouchTarget predecessor = null;

  135.             TouchTarget target = mFirstTouchTarget;

  136.             while (target != null) {

  137.                 final TouchTarget next = target.next;

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

  139.                     handled = true;

  140.                 } else {

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

  142.                             || intercepted;

  143.                     if (dispatchTransformedTouchEvent(ev, cancelChild,

  144.                             target.child, target.pointerIdBits)) {

  145.                         handled = true;

  146.                     }

  147.                     if (DBG_MOTION) {

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

  149.                                 + ",mFirstTouchTarget = " + mFirstTouchTarget + ",target = "

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

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

  152.                     }

  153.                     if (cancelChild) {

  154.                         if (predecessor == null) {

  155.                             mFirstTouchTarget = next;

  156.                         } else {

  157.                             predecessor.next = next;

  158.                         }

  159.                         target.recycle();

  160.                         target = next;

  161.                         continue;

  162.                     }

  163.                 }

  164.                 predecessor = target;

  165.                 target = next;

  166.             }

  167.         }

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

  169.         if (canceled

  170.                 || actionMasked == MotionEvent.ACTION_UP

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

  172.             resetTouchState();

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

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

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

  176.             removePointersFromTouchTargets(idBitsToRemove);

  177.         }

  178.     }

  179.     if (DBG_MOTION) {

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

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

  182.     }

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

  184.         mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);

  185.     }

  186.     return handled;

  187. }

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


本文名称:Android4.4Input模块笔记-创新互联
本文地址:http://ybzwz.com/article/dcgecd.html