目录
- Android事件分发的事件从何而来
- Activity的事件分发
- ViewRootImpl事件分发
- DecorView事件处理
Android事件分发的事件从何而来
事件分发一直以来都是一个android知识的重点。从应用开发角度和用户的交互就是在处理事件。
Activity的事件分发
事件分发一般情况都会讲view的分发过程,他的过程缩略起来就可以这样表示。
public boolean diapatchTouchEvent(MotionEvent ev) { | |
boolean consume = false; | |
if (onInterceptTouchEvent(ev)) { | |
consume = onTouchEvent(ev); | |
} else { | |
consume = child.dispatchTouchEvent(ev); | |
} | |
return consume; | |
} |
这里就有一个问题,最早的事件是从哪里来的。根据Android的视图模型知道最外层的view就是DecorView ,而它的外面是一个PhoneWindow。所以最初的事件就是从PhoneWindow进入了view的事件分发,而PhoneWindow的事件又是Activity中来的.
//frameworks/base/core/java/android/app/Activity.java | |
public boolean dispatchTouchEvent(MotionEvent ev) { | |
if (ev.getAction() == MotionEvent.ACTION_DOWN) { | |
onUserInteraction(); | |
} | |
if (getWindow().superDispatchTouchEvent(ev)) {//这里获取的PhoneWindow | |
return true; | |
} | |
return onTouchEvent(ev); | |
} |
那么问题又来了,activity的事件是哪里来的呢。
ViewRootImpl事件分发
熟悉Android的Window创建流程的话就知道ViewRootImpl是所有view的最顶层。也是ViewRootImpl在setView中实现了View和WindowManager之间的交互。这个方法里有一个在Window创建流程的时候没有关注的InputChannel,事件真正的来源就是它,在
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, | |
int userId) { | |
synchronized (this) { | |
if (mView == null) { | |
mView = view; | |
......... | |
InputChannel inputChannel = null;//创建InputChannel | |
if ((mWindowAttributes.inputFeatures | |
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { | |
inputChannel = new InputChannel(); | |
} | |
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes, | |
getHostVisibility(), mDisplay.getDisplayId(), userId, | |
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls, attachedFrame, sizeCompatScale);//将InputChannel传给WMS | |
if (inputChannel != null) { | |
if (mInputQueueCallback != null) { | |
mInputQueue = new InputQueue(); | |
mInputQueueCallback.onInputQueueCreated(mInputQueue); | |
} | |
mInputEventReceiver = new WindowInputEventReceiver(inputChannel, | |
Looper.myLooper());//创建mInputEventReceiver | |
} | |
//这里创建了各种事件处理器 | |
// Set up the input pipeline. | |
CharSequence counterSuffix = attrs.getTitle(); | |
mSyntheticInputStage = new SyntheticInputStage(); | |
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); | |
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, | |
"aq:native-post-ime:" + counterSuffix); | |
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); | |
InputStage imeStage = new ImeInputStage(earlyPostImeStage, | |
"aq:ime:" + counterSuffix); | |
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); | |
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, | |
"aq:native-pre-ime:" + counterSuffix); | |
mFirstInputStage = nativePreImeStage; | |
mFirstPostImeInputStage = earlyPostImeStage; | |
mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix; | |
AnimationHandler.requestAnimatorsEnabled(mAppVisible, this); | |
} | |
} | |
} |
从名字也能猜出mInputEventReceiver就是接收事件的对象了,这是一个ViewRootImpl的内部类看下它的实现。
final class WindowInputEventReceiver extends InputEventReceiver { | |
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { | |
super(inputChannel, looper); | |
} | |
@Override | |
public void onInputEvent(InputEvent event) {//通过名字就知道这应该是事件接收的回调 | |
List<InputEvent> processedEvents; | |
try { | |
processedEvents = | |
mInputCompatProcessor.processInputEventForCompatibility(event); | |
} finally { | |
Trace.traceEnd(Trace.TRACE_TAG_VIEW); | |
} | |
if (processedEvents != null) { | |
if (processedEvents.isEmpty()) { | |
// InputEvent consumed by mInputCompatProcessor | |
finishInputEvent(event, true); | |
} else { | |
for (int i = 0; i < processedEvents.size(); i++) { | |
enqueueInputEvent( | |
processedEvents.get(i), this, | |
QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true); | |
} | |
} | |
} else { | |
enqueueInputEvent(event, this, 0, true); | |
} | |
} | |
....... | |
} |
如果processedEvents不为空都是调用了enqueueInputEvent,不然就直接调用finishInputEvent。
void enqueueInputEvent(InputEvent event, | |
InputEventReceiver receiver, int flags, boolean processImmediately) { | |
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); | |
//这里做了区分是触摸事件还是按键事件 | |
if (event instanceof MotionEvent) { | |
MotionEvent me = (MotionEvent) event; | |
} else if (event instanceof KeyEvent) { | |
KeyEvent ke = (KeyEvent) event; | |
} | |
QueuedInputEvent last = mPendingInputEventTail; | |
if (last == null) { | |
mPendingInputEventHead = q; | |
mPendingInputEventTail = q; | |
} else { | |
last.mNext = q; | |
mPendingInputEventTail = q; | |
} | |
mPendingInputEventCount += 1; | |
if (processImmediately) { | |
doProcessInputEvents(); | |
} else { | |
scheduleProcessInputEvents(); | |
} | |
} | |
private void scheduleProcessInputEvents() { | |
if (!mProcessInputEventsScheduled) { | |
mProcessInputEventsScheduled = true; | |
Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS); | |
msg.setAsynchronous(true); | |
mHandler.sendMessage(msg); | |
} | |
} | |
private void handleMessageImpl(Message msg) { | |
switch (msg.what) { | |
case MSG_PROCESS_INPUT_EVENTS: | |
mProcessInputEventsScheduled = false; | |
doProcessInputEvents(); | |
} | |
} |
这里判断了是否要立即消费,如果立即消费doProcessInputEvents,不然调用scheduleProcessInputEvents。而scheduleProcessInputEvents很简单就是handle发送了一个异步消息。最后handle执行的时候还是会调用到doProcessInputEvents。所以就来详细看下doProcessInputEvents。
void doProcessInputEvents() { | |
// Deliver all pending input events in the queue. | |
while (mPendingInputEventHead != null) {//循环获取InputEvent并处理 | |
QueuedInputEvent q = mPendingInputEventHead; | |
mPendingInputEventHead = q.mNext; | |
if (mPendingInputEventHead == null) { | |
mPendingInputEventTail = null; | |
} | |
q.mNext = null; | |
mPendingInputEventCount -= 1; | |
mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent)); | |
deliverInputEvent(q); | |
} | |
//移除异步消息 | |
if (mProcessInputEventsScheduled) { | |
mProcessInputEventsScheduled = false; | |
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS); | |
} | |
} |
可以看到真实的处理都是deliverInputEvent来处理。
private void deliverInputEvent(QueuedInputEvent q) { | |
try { | |
if (mInputEventConsistencyVerifier != null) { | |
InputStage stage;//在ViewRootImpl的setView中初始化的处理器 | |
if (q.shouldSendToSynthesizer()) { | |
stage = mSyntheticInputStage; | |
} else { | |
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; | |
} | |
if (q.mEvent instanceof KeyEvent) { | |
try { | |
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); | |
} finally { | |
Trace.traceEnd(Trace.TRACE_TAG_VIEW); | |
} | |
} | |
if (stage != null) { | |
handleWindowFocusChanged(); | |
stage.deliver(q); | |
} else { | |
finishInputEvent(q); | |
} | |
} finally { | |
} | |
} |
在deliverInputEvent中出现了stage,这就是在setView初始化的那些处理器,处理通过stage.deliver(q)来实现。 InputStage 还是ViewRootImpl的一个内部类。
abstract class InputStage { | |
private final InputStage mNext; | |
protected static final int FORWARD = 0; | |
protected static final int FINISH_HANDLED = 1; | |
protected static final int FINISH_NOT_HANDLED = 2; | |
private String mTracePrefix; | |
public InputStage(InputStage next) { | |
mNext = next; | |
} | |
public final void deliver(QueuedInputEvent q) { | |
//分发事件 | |
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { | |
forward(q); | |
} else if (shouldDropInputEvent(q)) { | |
finish(q, false); | |
} else { | |
traceEvent(q, Trace.TRACE_TAG_VIEW); | |
final int result; | |
try { | |
result = onProcess(q); | |
} finally { | |
Trace.traceEnd(Trace.TRACE_TAG_VIEW); | |
} | |
apply(q, result); | |
} | |
} | |
//处理事件由子类改写 | |
protected int onProcess(QueuedInputEvent q) { | |
return FORWARD; | |
} | |
protected void finish(QueuedInputEvent q, boolean handled) { | |
q.mFlags |= QueuedInputEvent.FLAG_FINISHED; | |
if (handled) { | |
q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED; | |
} | |
forward(q); | |
} | |
protected void forward(QueuedInputEvent q) { | |
onDeliverToNext(q); | |
} | |
protected void onDeliverToNext(QueuedInputEvent q) { | |
//向后一个 InputStage 传递事件 | |
if (mNext != null) { | |
mNext.deliver(q); | |
} else { | |
finishInputEvent(q); | |
} | |
} | |
} |
熟悉okhttp的话很容易就发现这里也是一个责任链模式。从setView中 InputStage 子类的初始化也能看到,其中和view相关的是ViewPostImeInputStage。
final class ViewPostImeInputStage extends InputStage { | |
public ViewPostImeInputStage(InputStage next) { | |
super(next); | |
} | |
protected int onProcess(QueuedInputEvent q) { | |
if (q.mEvent instanceof KeyEvent) { | |
return processKeyEvent(q); | |
} else { | |
final int source = q.mEvent.getSource(); | |
//判断事件类型,触摸事件会进入processPointerEvent | |
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { | |
return processPointerEvent(q); | |
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { | |
return processTrackballEvent(q); | |
} else { | |
return processGenericMotionEvent(q); | |
} | |
} | |
} | |
private int processPointerEvent(QueuedInputEvent q) { | |
final MotionEvent event = (MotionEvent)q.mEvent; | |
mHandwritingInitiator.onTouchEvent(event); | |
mAttachInfo.mUnbufferedDispatchRequested = false; | |
mAttachInfo.mHandlingPointerEvent = true; | |
//通过mView的dispatchPointerEvent来分发事件 | |
boolean handled = mView.dispatchPointerEvent(event); | |
maybeUpdatePointerIcon(event); | |
maybeUpdateTooltip(event); | |
mAttachInfo.mHandlingPointerEvent = false; | |
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) { | |
mUnbufferedInputDispatch = true; | |
if (mConsumeBatchedInputScheduled) { | |
scheduleConsumeBatchedInputImmediately(); | |
} | |
} | |
return handled ? FINISH_HANDLED : FORWARD; | |
} |
ViewRootImpl的事件就交给mView来继续分发了,这里mView是DecorView,也是在setView中传进来的。
DecorView事件处理
//frameworks/base/core/java/android/view/View.java | |
public final boolean dispatchPointerEvent(MotionEvent event) { | |
if (event.isTouchEvent()) { | |
return dispatchTouchEvent(event); | |
} else { | |
return dispatchGenericMotionEvent(event); | |
} | |
} | |
//frameworks/base/core/java/com/android/internal/policy/DecorView.java | |
public boolean dispatchTouchEvent(MotionEvent ev) { | |
final Window.Callback cb = mWindow.getCallback(); | |
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 | |
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); | |
} |
这里通过dispatchTouchEvent将事件交给了Window.Callback,而这里的Window.Callback就是Activity,兜兜转转终于回到了Activity的dispatchTouchEvent中。
通过这个流程可以知道,事件的流程是WMS->ViewRootImpl->DecorView->Activity->PhoneWindow->DecorView,这里有一个疑问就是为什么不直接从DecorView开始分发。我猜测是为了方便在应用层重写Activity中的onTouch来消费没有view处理的事件。
现在还有一个疑问是WMS的事件是怎么来的,这个留着后续再分析。