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()->newConnection->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
publicfinalbooleandispatchPointerEvent(MotionEventevent){
if(event.isTouchEvent()){
returndispatchTouchEvent(event);
}else{
returndispatchGenericMotionEvent(event);
}
}
该方法继续调用dispatchTouchEvent(event),DecorView重新了该方法:
[java] view plaincopy
@Override
publicbooleandispatchTouchEvent(MotionEventev){
finalCallbackcb=getCallback();
returncb!=null&&!isDestroyed()&&mFeatureId<0?cb.dispatchTouchEvent(ev)
:super.dispatchTouchEvent(ev);
}
getCallback()函数获取apk注册的用于拦截按键、触摸等事件的回调函数。一般window不会拦截处理触摸事件,所以会继续调用super.dispatchTouchEvent(ev),即父类ViewGroup的dispatchTouchEvent()函数,在该函数中寻找到对应的View再继续调用dispatchTransformedTouchEvent()
[java] view plaincopy
for(inti=childrenCount-1;i>=0;i--){
finalintchildIndex=customOrder?
getChildDrawingOrder(childrenCount,i):i;
finalViewchild=children[childIndex];
if(!canViewReceivePointerEvents(child)
||!isTransformedTouchPointInView(x,y,child,null)){
continue;
}
newTouchTarget=getTouchTarget(child);
if(newTouchTarget!=null){
//Childisalreadyreceivingtouchwithinitsbounds.
//Giveitthenewpointerinadditiontotheonesitishandling.
newTouchTarget.pointerIdBits|=idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if(dispatchTransformedTouchEvent(ev,false,child,idBitsToAssign)){
//Childwantstoreceivetouchwithinitsbounds.
mLastTouchDownTime=ev.getDownTime();
mLastTouchDownIndex=childIndex;
mLastTouchDownX=ev.getX();
mLastTouchDownY=ev.getY();
newTouchTarget=addTouchTarget(child,idBitsToAssign);
alreadyDispatchedToNewTouchTarget=true;
break;
}
具体的分发规则可自行研究代码。
ViewGroup.dispatchTouchEvent()函数分析
[html] view plaincopy
@Override
publicbooleandispatchTouchEvent(MotionEventev){
if(mInputEventConsistencyVerifier!=null){
mInputEventConsistencyVerifier.onTouchEvent(ev,1);
}
if(DBG_MOTION||DBG_TOUCH){
Xlog.d(TAG,"(ViewGroup)dispatchTouchEvent1:ev="+ev+",mFirstTouchTarget="
+mFirstTouchTarget+",this="+this);
}
booleanhandled=false;
if(onFilterTouchEventForSecurity(ev)){
finalintaction=ev.getAction();
finalintactionMasked=action&MotionEvent.ACTION_MASK;
//Handleaninitialdown.
if(actionMasked==MotionEvent.ACTION_DOWN){
//Throwawayallpreviousstatewhenstartinganewtouchgesture.
//Theframeworkmayhavedroppedtheuporcanceleventforthepreviousgesture
//duetoanappswitch,ANR,orsomeotherstatechange.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
//Checkforinterception.
finalbooleanintercepted;
if(actionMasked==MotionEvent.ACTION_DOWN
||mFirstTouchTarget!=null){
finalbooleandisallowIntercept=(mGroupFlags&FLAG_DISALLOW_INTERCEPT)!=0;
if(!disallowIntercept){
intercepted=onInterceptTouchEvent(ev);
///M:addlogtohelpdebugging
if(intercepted==true){
if(DBG_TOUCH){
Xlog.d(TAG,"Toucheventwasinterceptedevent="+ev+",this="+this);
}
}
ev.setAction(action);//restoreactionincaseitwaschanged
}else{
intercepted=false;
}
}else{
//Therearenotouchtargetsandthisactionisnotaninitialdown
//sothisviewgroupcontinuestointercepttouches.
intercepted=true;
}
//Checkforcancelation.
finalbooleancanceled=resetCancelNextUpFlag(this)
||actionMasked==MotionEvent.ACTION_CANCEL;
//Updatelistoftouchtargetsforpointerdown,ifneeded.
finalbooleansplit=(mGroupFlags&FLAG_SPLIT_MOTION_EVENTS)!=0;
if(DBG_MOTION){
Xlog.d(TAG,"(ViewGroup)dispatchTouchEvent2:actionMasked="+actionMasked
+",intercepted="+intercepted+",canceled="+canceled+",split="
+split+",mChildrenCount="+mChildrenCount+",mFirstTouchTarget="
+mFirstTouchTarget+",this="+this);
}
TouchTargetnewTouchTarget=null;
booleanalreadyDispatchedToNewTouchTarget=false;
if(!canceled&&!intercepted){
if(actionMasked==MotionEvent.ACTION_DOWN
||(split&&actionMasked==MotionEvent.ACTION_POINTER_DOWN)
||actionMasked==MotionEvent.ACTION_HOVER_MOVE){
finalintactionIndex=ev.getActionIndex();//always0fordown
finalintidBitsToAssign=split?1<<ev.getPointerId(actionIndex)
:TouchTarget.ALL_POINTER_IDS;
//Cleanupearliertouchtargetsforthispointeridincasethey
//havebecomeoutofsync.
removePointersFromTouchTargets(idBitsToAssign);
finalintchildrenCount=mChildrenCount;
if(newTouchTarget==null&&childrenCount!=0){
finalfloatx=ev.getX(actionIndex);
finalfloaty=ev.getY(actionIndex);
//Findachildthatcanreceivetheevent.
//Scanchildrenfromfronttoback.
finalView[]children=mChildren;
finalbooleancustomOrder=isChildrenDrawingOrderEnabled();
for(inti=childrenCount-1;i>=0;i--){
finalintchildIndex=customOrder?
getChildDrawingOrder(childrenCount,i):i;
finalViewchild=children[childIndex];
if(!canViewReceivePointerEvents(child)
||!isTransformedTouchPointInView(x,y,child,null)){
if(DBG_MOTION){
Xlog.d(TAG,"(ViewGroup)dispatchTouchEventcontinue6:i="
+i+",count="+childrenCount+",child="+child
+",this="+this);
}
continue;
}
newTouchTarget=getTouchTarget(child);
if(DBG_MOTION){
Xlog.d(TAG,"(ViewGroup)dispatchTouchEventtochild3:child="
+child+",childrenCount="+childrenCount+",i="+i
+",newTouchTarget="+newTouchTarget+",idBitsToAssign="
+idBitsToAssign+",mFirstTouchTarget="+mFirstTouchTarget
+",this="+this);
}
if(newTouchTarget!=null){
//Childisalreadyreceivingtouchwithinitsbounds.
//Giveitthenewpointerinadditiontotheonesitishandling.
newTouchTarget.pointerIdBits|=idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if(dispatchTransformedTouchEvent(ev,false,child,idBitsToAssign)){
//Childwantstoreceivetouchwithinitsbounds.
mLastTouchDownTime=ev.getDownTime();
mLastTouchDownIndex=childIndex;
mLastTouchDownX=ev.getX();
mLastTouchDownY=ev.getY();
newTouchTarget=addTouchTarget(child,idBitsToAssign);
alreadyDispatchedToNewTouchTarget=true;
break;
}
}
}
if(newTouchTarget==null&&mFirstTouchTarget!=null){
//Didnotfindachildtoreceivetheevent.
//Assignthepointertotheleastrecentlyaddedtarget.
newTouchTarget=mFirstTouchTarget;
while(newTouchTarget.next!=null){
newTouchTarget=newTouchTarget.next;
}
newTouchTarget.pointerIdBits|=idBitsToAssign;
}
}
}
//Dispatchtotouchtargets.
if(mFirstTouchTarget==null){
//Notouchtargetssotreatthisasanordinaryview.
handled=dispatchTransformedTouchEvent(ev,canceled,null,
TouchTarget.ALL_POINTER_IDS);
}else{
//Dispatchtotouchtargets,excludingthenewtouchtargetifwealready
//dispatchedtoit.Canceltouchtargetsifnecessary.
TouchTargetpredecessor=null;
TouchTargettarget=mFirstTouchTarget;
while(target!=null){
finalTouchTargetnext=target.next;
if(alreadyDispatchedToNewTouchTarget&&target==newTouchTarget){
handled=true;
}else{
finalbooleancancelChild=resetCancelNextUpFlag(target.child)
||intercepted;
if(dispatchTransformedTouchEvent(ev,cancelChild,
target.child,target.pointerIdBits)){
handled=true;
}
if(DBG_MOTION){
Xlog.d(TAG,"dispatchTouchEventmiddle5:cancelChild="+cancelChild
+",mFirstTouchTarget="+mFirstTouchTarget+",target="
+target+",predecessor="+predecessor+",next="+next
+",this="+this);
}
if(cancelChild){
if(predecessor==null){
mFirstTouchTarget=next;
}else{
predecessor.next=next;
}
target.recycle();
target=next;
continue;
}
}
predecessor=target;
target=next;
}
}
//Updatelistoftouchtargetsforpointeruporcancel,ifneeded.
if(canceled
||actionMasked==MotionEvent.ACTION_UP
||actionMasked==MotionEvent.ACTION_HOVER_MOVE){
resetTouchState();
}elseif(split&&actionMasked==MotionEvent.ACTION_POINTER_UP){
finalintactionIndex=ev.getActionIndex();
finalintidBitsToRemove=1<<ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
if(DBG_MOTION){
Xlog.d(TAG,"(ViewGroup)dispatchTouchEventend4:handled="+handled+",mFirstTouchTarget="
+mFirstTouchTarget+",this="+this);
}
if(!handled&&mInputEventConsistencyVerifier!=null){
mInputEventConsistencyVerifier.onUnhandledEvent(ev,1);
}
returnhandled;
}
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。