在线客服
扫描二维码
下载博学谷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已经有了自己的答案了。如果觉得本文对你有所帮助,不妨把文章分享出去让更多的人看到。
— 申请免费试学名额 —
在职想转行提升,担心学不会?根据个人情况规划学习路线,闯关式自适应学习模式保证学习效果
讲师一对一辅导,在线答疑解惑,指导就业!
相关推荐 更多
数字化转型创新是什么?
数字化转型创新,企业对数字化的需求越来越大,企业管理者们逐渐了解各种新兴的数字化技术,数字化转型则是面临着原生数字化公司竞争的传统企业,怎样运用数字化手段来改造运营建立商业生态。
17132
2019-12-04 15:24:51
5G网络与APP移动应用开发的发展趋势如何?
移动应用渗透到人类日常生活。排除那些还没普及智能手机的国家与地区,平均每个用户的智能手机上都搭载着1个以上的应用程序。5G技术在家庭和企业中引入互联和永远在线设备的新世界,实时共享数据并享受新的速度和连接可靠性标准。
8008
2020-02-14 18:13:37
大学生需要IT培训吗?
现如今,大学生是国家有需人才,而科技的快速发展正是缺少市场,大学生需要IT培训吗?答案是肯定。根据目前从培训机构数据来说,不少人确实找到了优质的工作,月薪起步也是不菲的。但这不是全部,毕竟培训机构总有优秀和差等生。属于你的IT就要靠自己争取。
6988
2020-03-26 14:34:14
入门IT行业需要了解什么?
入门IT行业需要了解什么?各行各业都有它的一个专属基础知识,在你学习前后都是必须拥有的。经验占据了很大一部分的空间,逻辑思维能力是指对基础的灵活掌握和变通。每一个人编写程序的角度和考虑的范围都不尽相同,所有代码有优有略。关于入门IT行业的注意事项:
8028
2020-06-09 11:52:16
你与架构师相比差距在哪儿?如何提升
你与架构师相比差距在哪儿?如何提升?程序员从开发做起,工作前3-5年可以是中级开发,但随着年限的上升还停留在开发上那么淘汰是迟早的事。在IT互联网行业除了保持不断学习,还要规划好自己的职业生涯。
5358
2022-03-23 10:19:59
