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

总结: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. }
时间: 2024-08-30 03:52:10

Android4.4   Input模块笔记的相关文章

Android4.4之Input模块笔记

在InputReader从EventHub中获取输入事件,包含触摸屏事件.物理按键事件等,然后转交给InputDispatcher线程,InputDispatcher经过筛选,过滤输入事件.对于触摸事件通过调用findTouchedWindowTargetsLocked()函数找到合适的InputTarget,然后通过dispatchEventLocked()->prepareDispatchCycleLocked()->enqueueDispatchEntriesLocked()->e

Android4.0 input事件输入流程详解(中间层到应用层)

在Android系统中,类似于键盘按键.触摸屏等事件是由WindowManagerService服务来管理的,然后再以消息的形式来分发给应用程序进行处理.系统启动时,窗口管理服务也会启动,该服务启动过程中,会通过系统输入管理器InputManager来负责监控键盘消息.当某一个Activity激活时,会在该Service下注册一个接收消息的通道,表明可以处理具体的消息,然后当有消息时,InputManager就会分发给当前处于激活状态下的Activity进行处理. InputManager的启动

Django模块笔记【一】

入门笔记翻译整理自:https://docs.djangoproject.com/en/1.8/topics/ *该笔记将对各个模块进行单独介绍 *Model&Database 1. 模型(models) 模型是数据的唯一信息源,它指示了数据的域(fields)和行为(behaviors).每个模型都对应一个数据库表. ①每个模型都是django.db.models.Model的子类:②模型的每个属性代表了数据库的域:③数据库入口API,参考making queries部分. 比如, 1 fro

Django模块笔记【三】

入门笔记翻译整理自:https://docs.djangoproject.com/en/1.8/topics/ *该笔记将对各个模块进行单独介绍 *Forms 1. 使用表单(Working with forms) 只要网站涉及到访问者的输入操作,那么就必须用到表单.在HTML中,表单是<form>...</form>中的语句集合. GET和POST是HTTP处理表单仅有的两种方式.Django中使用Form类表示表单. 对使用方法进行简单举例: 1 # forms.py 2 3

ldd3-2 构造和运行模块:Hello World模块笔记

实验环境: 按照之前的搭建方法,已经在Ubuntu 5.04版本上构建了linux原始的2.6.10版本内核树: GCC是用的安装镜像自带的版本: 一切准备就绪后对虚拟机做了快照,防止内核损坏: 因为Ubuntu 5.04虚拟机下编程很麻烦,所以编码和调试都不在虚拟机下运行了: 编辑在windows下运行,然后把代码文件通过Xftp传输到虚拟机里: 调试的话通过Xshell: 笔记基本是按照书上小结的标题来的,每个标题能做实验的就做实验,理论性的就小结一下,不易过多记忆,因为没代码实践,理论也理

Django模块笔记【六】

入门笔记翻译整理自:https://docs.djangoproject.com/en/1.8/topics/ *该笔记将对各个模块进行单独介绍 *migration&Managing files&Testing in Django 1. migration的命令 migrate命令负责应用migrations,同时也负责撤销migrations以及查看他们的状态. makemigrations基于对模型的改变,创建新的migrations. sqlmigrate为migration显示S

Django模块笔记【二】

入门笔记翻译整理自:https://docs.djangoproject.com/en/1.8/topics/ *该笔记将对各个模块进行单独介绍 * HTTP Request Handlers 1. URL dispatcher URL在名为URLconf的Python模块中创建,该模块提供了URL模式(正则表达式)和Python函数(视图)之间的映射. URLconf举例如下: 1 from django.conf.urls import url 2 3 from . import views

Django模块笔记【五】

入门笔记翻译整理自:https://docs.djangoproject.com/en/1.8/topics/ *该笔记将对各个模块进行单独介绍 *Class-based views 视图是接受请求并返回响应的可调用对象. 1. 用法举例 1 from django.conf.urls import url 2 from django.views.generic import TemplateView 3 4 urlpatterns = [ 5 url(r'^about/', TemplateV

Django模块笔记【四】

入门笔记翻译整理自:https://docs.djangoproject.com/en/1.8/topics/ *该笔记将对各个模块进行单独介绍 *template 1. 配置(Configuration) 1 TEMPLATES = [ 2 { 3 'BACKEND': 'django.template.backends.django.DjangoTemplates', 4 'DIRS': [], 5 'APP_DIRS': True, 6 'OPTIONS': { 7 # ... some