ViewRootImpl事件队列过程
InputChannel的回调
- ViewRootImpl中,InputChannel的事件来来去去,最终回调到了其内部类WindowInputEventReceiver,在WindowInputEventReceiver中,回调onInputEvent()函数。
- 经过一系列的队列操作,最终又回调到了ViewRootImpl中来。因此,我们突然明白,为什么InputEventReceiver的实现类WindowInputEventReceiver要是现在ViewRootImpl的内部来。
- 这样做的好处是,事件来来去去,都能来到View的树根。这也就呼应了很多事件分发的文章都说,事件的传递首先是从父亲往下传,一直到子子孙孙辈的。
ViewRootImpl入口:
- WindowInputEventReceiver中的onInputEvent()函数,只有一个操作,调用了ViewRootImpl中的以下方法:
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
...
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);//InputEvent在这里被封装成了QueuedInputEvent
...
if (processImmediately) {//因为在onInputEvent中,调用该函数的参数是true
doProcessInputEvents();//因此,立刻执行了这一步逻辑
} else {
scheduleProcessInputEvents();
}
}
- QueuedInputEvent的存在,是用于构造一个通信回路的:
- QueuedInputEvent的构造,传入的InputEvent,InputEventReceiver。
- 因此,可以想象到,当这个对象在队列中被处理了以后,它会回调InputEventReceiver的finishInputEvent(),告诉服务端,我处理完这个事件了。
- 因为onInputEvent传入的参数恒为true,因此事件是立即被处理的,所以回调了doProcessInputEvents()函数
ViewRootImpl的分发:
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
while (mPendingInputEventHead != null) {//只要队列头不为空,就将队列中的事件一次性处理完,这样的方式,能够快速处理积压的事件
...
deliverInputEvent(q);//最终回调到这里,单个的事件,开始正式被处理
}
...
}
- 由此可以知道,一大串的事件进来的时候,他们在队列中排好了队,因而不至于出现错乱的问题。接下来我们进入处理内部再分析分析
- 传递事件,我们这里用的是传递事件,而不是分发,主要是因为,此时,事件的走向,其实是链式的:
private void deliverInputEvent(QueuedInputEvent q) {
...
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;//主要逻辑在这里,这里决定了事件是否需要率先交给输入法进行处理。
}
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);//果不其然,在这里,队列事件QueuedInputEvent通知了服务端,事件被处理完了。
}
}
InputStage
- 这个类的名称大致是:输入层次的意思。感觉像是处理事件是分层的,我们先来分析这个类。
abstract class InputStage {
private final InputStage mNext;//单向链表的下一个对象
protected static final int FORWARD = 0;//转发
protected static final int FINISH_HANDLED = 1;//处理完成
protected static final int FINISH_NOT_HANDLED = 2;//没处理
/**
,* Creates an input stage.
,* @param next The next stage to which events should be forwarded.
,*/
public InputStage(InputStage next) {
mNext = next;//构造时,传入下一个事件的转发对象,看着应该是个单向链表
}
...
}
- 从这里看,InputStage是一个单向链表,存着排在自己后面的对象。
- 它的三个常量,表示事件的处理结果:被转发、被处理、没被处理
- 从这里可以大概知道,事件交给InputStage对象的时候,如果没有处理,可能就会被转发给它的队列后方。
- 因此,呼应了我们一开始说的,链式调用
InputStage的实现类:
- ViewRootImpl通过InputStage创建了一个队列,通过遍寻队列的方式,让InputStage对象对事件进行处理。
- 那么我们来看下,都有哪些InputStage在队列中进行等待:
InputStage mFirstInputStage;//输入法预处理队列的表头
InputStage mFirstPostImeInputStage;//不经输入法队列的表头
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);//表示传递不经输入法的事件给ViewTree
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);//表示传递不经输入法的事件给NativeActivity
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);//表示早起的不经输入法事件的一些预处理过程
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);//传递输入事件给输入法
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);//传递事件给ViewTree先让输入法预处理
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);//传递事件给NativeActivity先让输入法预处理
mFirstInputStage = nativePreImeStage;//输入法预处理队列的表头赋值
mFirstPostImeInputStage = earlyPostImeStage;//不经输入法队列的表头赋值
...
}
- 从这里可以看出来,ViewRootImpl处理事件,有两个队列。一个是需要将事件先交给输入法预处理的队列,另一个是不需要的。如果输入法处理了事件,那么就不会继续下发了。
回到ViewRootImpl:
- 回到ViewRootImpl的deliverInputEvent()中,我们再来看下:
private void deliverInputEvent(QueuedInputEvent q) {
...
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;//主要逻辑在这里,这里决定了事件是否需要率先交给输入法进行处理。
//这里的逻辑,询问了事件是否需要跳过输入法,如果跳过,则调用不许预处理的队列,否则反之
}
if (stage != null) {
stage.deliver(q);//获取到事件的处理队列以后,就开始在队列中遍历了。
} else {
finishInputEvent(q);//果不其然,在这里,队列事件QueuedInputEvent通知了服务端,事件被处理完了。
}
}
//是否跳过输入法的判断
public boolean shouldSkipIme() {
if ((mFlags & FLAG_DELIVER_POST_IME) != 0) {//如果有明确指明跳过的flag,就直接跳过了
return true;
}
//否则如果是MotionEvent,并且来自鼠标指针、屏幕触摸,都是要跳过输入法的
return mEvent instanceof MotionEvent
&& (mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
|| mEvent.isFromSource(InputDevice.SOURCE_ROTARY_ENCODER));
}
在进入InputStage:
- 事件不论如何总要选择一个队列,再次进入到InputStage的实现类逻辑中。
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {//如果拥有事件完成的flag,就表示这个类不需要在对其进行处理了
forward(q);//对事件进行转发
//转发事件的逻辑是调用另一个函数onDeliverToNext(q);
} else if (shouldDropInputEvent(q)) {//如果需要抛弃事件
finish(q, false);//直接完成
//finish的逻辑很好理解,添加事件完成的标签,如果已经被处理过,那再加上完成和处理的标签
} else {
apply(q, onProcess(q));//先看自己是否要处理,随后调用apply进行下一步操作
//onProcess()的逻辑一般在子类内部实现,事件队列过程中,是否被处理,基本都在这里可以知道,我们重点讨论这个函数
}
}
protected void onDeliverToNext(QueuedInputEvent q) {
if (DEBUG_INPUT_STAGES) {
Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
}
if (mNext != null) {
mNext.deliver(q);//如果自己不是队尾,向下转发
} else {
finishInputEvent(q);//否则完成事件
}
}
protected void apply(QueuedInputEvent q, int result) {
//apply其实就是确认事件是否已经被处理、或者需要进一步转发的过程
if (result == FORWARD) {
forward(q);
} else if (result == FINISH_HANDLED) {
finish(q, true);
} else if (result == FINISH_NOT_HANDLED) {
finish(q, false);
} else {
throw new IllegalArgumentException("Invalid result: " + result);
}
}
- 我们可以看到,deliverInputEvent()函数中调用stage.deliver(q),即首先进入的就是InputStage的deliver()函数.
- deliver()函数首先确认事件是否已经处理完(判断是否有处理完成的flag),有的话传给后续队列
- 否则就确认是否要抛弃这个事件,如果要抛弃,就调用finish()给事件添加标签。
- 如果决定不抛弃不放弃,那就问下自己的onProcess()函数,是不是要处理相应的事件。onProcess()函数通常在子类中实现,ViewRootImpl的事件分发,就是靠它完成的,我们后面详细讨论
- 等待onProcess()执行完,返回一个int结果,在apply()函数中,来确认是处理完了还是没处理完。再考虑事件是否应该往下转发等等。
- 整个InputStage队列的处理流程就是这样的。接下来我们再详细看下,InputStage的一些主要的实现类的onProcess()函数都做了一些什么。