您的位置:首页 > 移动开发 > Android开发

Android4.4   Input模块笔记

2014-11-25 10:04 246 查看
在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
public final boolean dispatchPointerEvent(MotionEvent event) {

if (event.isTouchEvent()) {

return dispatchTouchEvent(event);

} else {

return dispatchGenericMotionEvent(event);

}

}

该方法继续调用dispatchTouchEvent(event),DecorView重新了该方法:
[java] view plaincopy
@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

final Callback cb = getCallback();

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

: super.dispatchTouchEvent(ev);

}

getCallback()
函数获取apk注册的用于拦截按键、触摸等事件的回调函数。一般window不会拦截处理触摸事件,所以会继续调用
super.dispatchTouchEvent(ev),即父类ViewGroup的dispatchTouchEvent()函数,在该函数中寻找
到对应的View再继续调用dispatchTransformedTouchEvent()
[java] view plaincopy
for (int i = childrenCount - 1; i >= 0; i--) {

final int childIndex = customOrder ?

getChildDrawingOrder(childrenCount, i) : i;

final View child = children[childIndex];

if (!canViewReceivePointerEvents(child)

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

continue;

}

newTouchTarget = getTouchTarget(child);

if (newTouchTarget != null) {

// Child is already receiving touch within its bounds.

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

newTouchTarget.pointerIdBits |= idBitsToAssign;

break;

}

resetCancelNextUpFlag(child);

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

// Child wants to receive touch within its bounds.

mLastTouchDownTime = ev.getDownTime();

mLastTouchDownIndex = childIndex;

mLastTouchDownX = ev.getX();

mLastTouchDownY = ev.getY();

newTouchTarget = addTouchTarget(child, idBitsToAssign);

alreadyDispatchedToNewTouchTarget = true;

break;

}

具体的分发规则可自行研究代码。
ViewGroup.dispatchTouchEvent()函数分析

[html] view plaincopy
@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onTouchEvent(ev, 1);

}

if (DBG_MOTION || DBG_TOUCH) {

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

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

}

boolean handled = false;

if (onFilterTouchEventForSecurity(ev)) {

final int action = ev.getAction();

final int actionMasked = action & MotionEvent.ACTION_MASK;

// Handle an initial down.

if (actionMasked == MotionEvent.ACTION_DOWN) {

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

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

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

cancelAndClearTouchTargets(ev);

resetTouchState();

}

// Check for interception.

final boolean intercepted;

if (actionMasked == MotionEvent.ACTION_DOWN

|| mFirstTouchTarget != null) {

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

if (!disallowIntercept) {

intercepted = onInterceptTouchEvent(ev);

/// M : add log to help debugging

if (intercepted == true) {

if (DBG_TOUCH) {

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

}

}

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

} else {

intercepted = false;

}

} else {

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

// so this view group continues to intercept touches.

intercepted = true;

}

// Check for cancelation.

final boolean canceled = resetCancelNextUpFlag(this)

|| actionMasked == MotionEvent.ACTION_CANCEL;

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

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

if (DBG_MOTION) {

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

+ ",intercepted = " + intercepted + ",canceled = " + canceled + ",split = "

+ split + ",mChildrenCount = " + mChildrenCount + ",mFirstTouchTarget = "

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

}

TouchTarget newTouchTarget = null;

boolean alreadyDispatchedToNewTouchTarget = false;

if (!canceled && !intercepted) {

if (actionMasked == MotionEvent.ACTION_DOWN

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

|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

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

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

: TouchTarget.ALL_POINTER_IDS;

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

// have become out of sync.

removePointersFromTouchTargets(idBitsToAssign);

final int childrenCount = mChildrenCount;

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

final float x = ev.getX(actionIndex);

final float y = ev.getY(actionIndex);

// Find a child that can receive the event.

// Scan children from front to back.

final View[] children = mChildren;

final boolean customOrder = isChildrenDrawingOrderEnabled();

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

final int childIndex = customOrder ?

getChildDrawingOrder(childrenCount, i) : i;

final View child = children[childIndex];

if (!canViewReceivePointerEvents(child)

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

if (DBG_MOTION) {

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

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

+ ",this = " + this);

}

continue;

}

newTouchTarget = getTouchTarget(child);

if (DBG_MOTION) {

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

+ child + ",childrenCount = " + childrenCount + ",i = " + i

+ ",newTouchTarget = " + newTouchTarget + ",idBitsToAssign = "

+ idBitsToAssign + ",mFirstTouchTarget = " + mFirstTouchTarget

+ ",this = " + this);

}

if (newTouchTarget != null) {

// Child is already receiving touch within its bounds.

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

newTouchTarget.pointerIdBits |= idBitsToAssign;

break;

}

resetCancelNextUpFlag(child);

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

// Child wants to receive touch within its bounds.

mLastTouchDownTime = ev.getDownTime();

mLastTouchDownIndex = childIndex;

mLastTouchDownX = ev.getX();

mLastTouchDownY = ev.getY();

newTouchTarget = addTouchTarget(child, idBitsToAssign);

alreadyDispatchedToNewTouchTarget = true;

break;

}

}

}

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

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

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

newTouchTarget = mFirstTouchTarget;

while (newTouchTarget.next != null) {

newTouchTarget = newTouchTarget.next;

}

newTouchTarget.pointerIdBits |= idBitsToAssign;

}

}

}

// Dispatch to touch targets.

if (mFirstTouchTarget == null) {

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

handled = dispatchTransformedTouchEvent(ev, canceled, null,

TouchTarget.ALL_POINTER_IDS);

} else {

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

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

TouchTarget predecessor = null;

TouchTarget target = mFirstTouchTarget;

while (target != null) {

final TouchTarget next = target.next;

if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {

handled = true;

} else {

final boolean cancelChild = resetCancelNextUpFlag(target.child)

|| intercepted;

if (dispatchTransformedTouchEvent(ev, cancelChild,

target.child, target.pointerIdBits)) {

handled = true;

}

if (DBG_MOTION) {

Xlog.d(TAG, "dispatchTouchEvent middle 5: 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;

}

}

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

if (canceled

|| actionMasked == MotionEvent.ACTION_UP

|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

resetTouchState();

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

final int actionIndex = ev.getActionIndex();

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

removePointersFromTouchTargets(idBitsToRemove);

}

}

if (DBG_MOTION) {

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

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

}

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

mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);

}

return handled;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Input Android4.4