在线客服
扫描二维码
下载博学谷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已经有了自己的答案了。如果觉得本文对你有所帮助,不妨把文章分享出去让更多的人看到。
— 申请免费试学名额 —
在职想转行提升,担心学不会?根据个人情况规划学习路线,闯关式自适应学习模式保证学习效果
讲师一对一辅导,在线答疑解惑,指导就业!
相关推荐 更多
给网站更换服务器 需要注意这几点
服务器对于网站的重要性不言而喻,网站的顺利运行离不开服务器。那么给网站更换服务器需要注意什么呢?主要做到这几点:关闭网站;网站备份;检测新的站点空间;不要盲目频繁搬迁。下面详细为大家进一步解释。
6087
2019-07-29 11:59:28
计算机培训班有用吗?哪些人适合报培训班?
关于计算机培训班是否有用要根据自己的情况出发,如果是零基础学员小编认为参加计算机培训班非常有用也很有必要。培训机构为整个IT行业培养了大量的专业人才,不少IT行业的从业者都有过培训的经历,总体来说计算机培训比较靠谱。
11211
2020-04-20 15:25:48
IT行业发展前景如何?
IT行业发展前景如何?IT行业可以说算是近几年比较热的领域,这一点无论从大学专业的选择还是专业培训班的火热上都可以看出来。很多人选择IT行业,就是觉得好就业、工资高。但是也有一些人会担心,这么多人涌入这个IT行业,人才会不会饱和呢?今天就和大家谈谈IT行业发展前景吧。
4401
2020-05-29 14:20:26
学IT大概需要多少费用?
学IT大概需要多少费用?按照目前培训市场的行情,IT培训的学费最低也要上万元,根据地区经济发展水平和不同的学科内容,一般都在25K之内。线上的IT课程普遍会比线下的面授课便宜很多,毕竟省去了场地的大头成本。另外,不同的培训科目内容,培训价格也有所区别。不过比起学费,选择一个靠谱的培训机构和自己喜欢擅长的科目更重要!
4880
2020-07-07 17:23:39
你与架构师相比差距在哪儿?如何提升
你与架构师相比差距在哪儿?如何提升?程序员从开发做起,工作前3-5年可以是中级开发,但随着年限的上升还停留在开发上那么淘汰是迟早的事。在IT互联网行业除了保持不断学习,还要规划好自己的职业生涯。
2408
2022-03-23 10:19:59