在线客服
扫描二维码
下载博学谷APP
扫描二维码
关注博学谷微信公众号
事件是先到DecorView还是先到Window?答案是显而易见的,事件当然是先到DecorView。为什么呢?这就需要我们弄清楚DecorView、PhoneWindow和Activity/Dialog之间传递的顺序,下面我们来看看Input系统、Framework层、DecorView和Activity的相关内容,相信大家就能理解事件先到DecorView的本质原因了。

1、Input系统
当用户触摸屏幕或者按键操作,首次触发的是硬件驱动,驱动收到事件后,将该相应事件写入到输入设备节点,这便产生了最原生态的内核事件。接着,输入系统取出原生态的事件,经过层层封装后成为KeyEvent或者MotionEvent ;最后,交付给相应的目标窗口(Window)来消费该输入事件。
(1)当屏幕被触摸,Linux内核会将硬件产生的触摸事件包装为Event存到/dev/input/event[x]目录下。
(2)Input系统—InputReader线程:loop起来让EventHub调用getEvent()不断的从/dev/input/文件夹下读取输入事件。然后转换成EventEntry事件加入到InputDispatcher的mInboundQueue。
(3)Input系统—InputDispatcher线程:从mInboundQueue队列取出事件,转换成DispatchEntry事件加入到connection的outboundQueue队列。再然后开始处理分发事件 (比如分发到ViewRootImpl的WindowInputEventReceiver中),取出outbound队列,放入waitQueue.
(4)Input系统—UI线程:创建socket pair,分别位于”InputDispatcher”线程和focused窗口所在进程的UI主线程,可相互通信。
2、Framework层
//InputEventReceiver.dispachInputEvent()
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
Native层通过JNI执行Framework层的InputEventReceiver.dispachInputEvent(),而真正调用的是继承了InputEventReceiver的ViewRootImpl.WindowInputEventReceiver。所以这里执行的WindowInputEventReceiver的dispachInputEvent():
final class WindowInputEventReceiver extends InputEventReceiver {
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
...
}
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事件:
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);
...
}
3、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。
4、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();不直接分发给DecorView,而是要通过PhoneWindow来间接发送也是因为Activity不知道有DecorView!但是,Activity持有PhoneWindow ,而PhoneWindow当然知道自己的窗口里有些什么了,所以能够把事件派发给DecorView。在Android中,Activity并不知道自己的Window中有些什么,这样耦合性就很低了。不管Window里面的内容如何,只要Window仍然符合Activity制定的标准,那么它就能在Activity中很好的工作。当然,这就是解耦所带来的扩展性的好处。
看到这里,想必大家对于事件是先到DecorView还是先到Window已经有了自己的答案了。如果觉得本文对你有所帮助,不妨把文章分享出去让更多的人看到。
— 申请免费试学名额 —
在职想转行提升,担心学不会?根据个人情况规划学习路线,闯关式自适应学习模式保证学习效果
讲师一对一辅导,在线答疑解惑,指导就业!
相关推荐 更多
网络安全大赛真的存在吗?网络安全大赛科普
最近引发收视狂潮的国产电视剧《亲爱的,热爱的》已经结局了,相信大家在嗑男女主CP的同时,也注意到了贯穿全剧的网络安全大赛。不少人第一次听到这个比赛的时候,会有这样的困惑:网络安全大赛真的存在吗?其实,这个比赛还真不是编剧瞎编的,网络安全大赛真的存在。下面小编辑详细介绍一下它。
89217
2019-08-23 14:17:33
疫情对互联网格局的影响解析
疫情之下,线下实体经济发展受到剧烈冲击的同时,各大互联网公司都在此次灾情面前面临巨大的挑战和机遇。受疫情影响,闭门不出的用户与互联网有了更长时间的深入接触,因此我们可以看到疫情改变了我们的许多生活习惯。比如,用户对社交、资讯平台的更频繁使用,在线远程办公和线上学习软件的用户暴涨,生鲜电商行业的异军突起,以及传统线下实体店营销模式向线上转型的调整等等。下面我们就来具体分析一下疫情对互联网格局的影响。
10414
2020-03-02 10:59:14
疫情过后医疗IT行业发展趋势如何?
疫情过后医疗IT行业发展趋势如何?IT技术利用现有各种格式的电子数据开展清洗导入、数据采集,以合适简单的可视化方式直观展示,引入外部门辅助决策数据,指导疫情监控,在疫情期间IT技术发挥了重要作用。
6888
2020-03-24 11:26:56
零基础转行IT如何选择岗位?
毫不夸张的说,IT行业是为数不多可以通过自身奋斗完全改变自己命运的通道。为什么这么说呢?最直观的薪资待遇早已说明了一切,IT行业起薪都在7K~8K的大环境下,可以说抵得上许多行业好几年年的努力。这也是为什么许多人奋不顾身地投入这个行业的原因。那么,常常困扰新人的一个问题就是,零基础转行IT如何选择岗位?下面我们一起来具体分析一下,看看那个岗位最适合你。
6874
2020-04-09 20:42:59
低代码开发是什么?会被取代吗?
低代码开发平台功能愈发强大,如此强大的低代码未来是否会取代开发人员?低代码旨在替代可重复的过程与功能,而即使它可以达到一个开发者 99% 的水平,但最后的1%才是至关重要的一步,特定的功能还是需要开发者的手写代码。
6479
2021-01-15 14:23:23
