写点什么

Android 知识笔记:记录 2 个 “容易误解,网易的朋友给我这份 339 页的 Android 面经

用户头像
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


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


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 中有些什么,这样耦合性就很低了。

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android知识笔记:记录 2 个 “容易误解,网易的朋友给我这份339页的Android面经