Android 知识笔记:记录 2 个 “容易误解,网易的朋友给我这份 339 页的 Android 面经
}
ViewRootImpl
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
...
if (processImmediately) {
//关键点:执行 Input 事件
doProcessInputEvents();
} else {
//走一遍 Handler 延迟处理事件
scheduleProcessInputEvents();
}
}
void doProcessInputEvents() {
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
long eventTime = q.mEvent.getEventTimeNano();
long oldestEventTime = eventTime;
if (q.mEvent instanceof MotionEvent) {
MotionEvent me = (MotionEvent)q.mEvent;
if (me.getHistorySize() > 0) {
oldestEventTime = me.getHistoricalEventTimeNano(0);
}
}
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
//关键点:进一步派发事件处理
deliverInputEvent(q);
}
...
}
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (stage != null) {
//关键点:上面决定将事件派发到那个 InputStage 中处理
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
ViewRootImpl.ViewPostImeInputStage
前面事件会派发到 ViewRootImpl.ViewPostImeInputStage 中处理,它的父类 InputStage.deliver()方法会调用 apply()来处理 Touch 事件:
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
//关键点:执行分发 touch 事件
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;
...
//关键点:mView 分发 Touch 事件,mView 就是 DecorView
boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
...
}
DecorView
如果你熟悉安卓的 Window,Activity 和 Dialog 对应的 ViewRootImpl 成员 mView 就是 DecorView,View 的 dispatchPointerEvent()代码如下:
//View.java
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
//分发 Touch 事件
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
因为 DecorView 继承 FrameLayout,上面所以会调用 DecorView 的 dispatchTouchEvent():
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final
Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
上面 Window.Callback 都被 Activity 和 Dialog 实现,所以变量 cb 可能就是 Activity 和 Dialog。
Activity
当上面 cb 是 Activity 时,执行 Activity 的 dispatchTouchEvent():
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {//关键点:getWindow().superDispatchTouchEvent(ev)
return true;
}
return onTouchEvent(ev);
}
如果你熟悉安卓的 Window,Activity 的 getWindow()拿到的就是 PhoneWindow,下面是 PhoneWindow 的代码:
//PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//调用 DecorView 的 superDispatchTouchEvent
return mDecor.superDispatchTouchEvent(event);
}
下面是 DecorView.superDispatchTouchEvent()代码:
//DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event) {
//调用 ViewGroup 的 dispatchTouchEvent()开始我们常见的分发 Touch 事件
return super.dispatchTouchEvent(event);
}
流程图
答:为什么要 DecorView -> Activity -> PhoneWindow -> DecorView 传递事件?
解耦!
ViewRootImpl 并不知道有 Activity 这种东西存在!它只是持有了 DecorView。
所以,不能直接把触摸事件送到 Activity.dispatchTouchEvent();
那么,既然触摸事件已经到了 Activity.dispatchTouchEvent()中了,为什么不直接分发给 DecorView,而是要通过 PhoneWindow 来间接发送呢?
因为 Activity 不知道有 DecorView!但是,Activity 持有 PhoneWindow ,而 PhoneWindow 当然知道自己的窗口里有些什么了,所以能够把事件派发给 DecorView。
在 Android 中,Activity 并不知道自己的 Window 中有些什么,这样耦合性就很低了。
评论