在Android Tv上使用遥控器在弹出的软键盘上面进行字符的选择和输入,这个Input事件的传递过程是怎样的?输入法的窗口是没有获取焦点的,真正有焦点的还是把输入法窗口给启动起来的TextView,Input框架那边有Input事件还是通过socket传递给在ViewRootImpl注册过的客户端的InputChannel,那之后是怎么处理的。在启动InputMethodService的过程中创建Session时有创建一对InputChannel,这个明显是用来进行input事件通信的,那究竟是如何通信的。找了一些文章都没有提到这块,自己梳理下。
代码分析基于4.4,现在P的代码有变动,但是基本流程是一样的。
一、应用客户端传递Input事件给InputMethodManager
ViewRootImpl.setView,Activity创建时经ActivityThread::handleResumeActivity-->WindowManagerGlobal::addView-->ViewRootImpl::setView,最终会基于之前创建的一堆InputChannel的客户端创建自己的InputEventReceiver,然后在主线程上等待Input Event的到来。
ActivityThread::handleResumeActivity
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
WindowManagerGlobal::addView
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
ViewRootImpl::setView
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
WindowInputEventReceiver如何处理到来的Input事件
在native侧由looper监听InputChannel对应的fd,当有时间来临时会调用WindowInputEventReceiver的callback onInputEvent来进行处理。
native侧如何进行监听的可以参考gityuan的博客http://gityuan.com/2016/12/31/input-ipc/
ViewRoot::WindowInputEventReceiver::onInputEvent
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
scheduleConsumeBatchedInput();
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
WindowInputEventReceiver mInputEventReceiver;
先将InputEvent加入队列,然后进行处理。
ViewRootImpl::doProcessInputEvents
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
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);
deliverInputEvent(q);
}
// We are done processing all input events that we can process right now
// so we can clear the pending flag immediately.
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
事件分发,之前只是知道这里往后就是层层分发直到最终的View,实际上关键就在于这个InputStage。它实际上不是单独就deliver一下就完了的,而是好多个InputStage的子类如同接力一般每个都process一遍才最终分发出去的,而分发给输入法事件的的关键也在这里。下面看看这个mFirstPostImeInputStage。
ViewRootImpl::deliverInputEvent
private void deliverInputEvent(QueuedInputEvent q) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
try {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
set up input pipeline,说的真好。
ViewRootImpl::setView
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
InputStage syntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
InputStage表示处理input的不同阶段。每个InputStage子类实例的时候都会把要接力的下一棒赋值到自己的mNext成员里面,然后在deliver时会先forward一下在onDeliverToNext时调用mNext的deliver方法进行接力处理,所以mFirstPostImeInputStage在分发事件时,是一定会交给中间的ImeInputStage来处理一下的,也就是onProcess方法。
ViewRootImpl::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;
}
/**
* Delivers an event to be processed.
*/
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
apply(q, onProcess(q));
}
}
/**
* Marks the the input event as finished then forwards it to the next stage.
*/
protected void finish(QueuedInputEvent q, boolean handled) {
q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
if (handled) {
q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
}
forward(q);
}
/**
* Forwards the event to the next stage.
*/
protected void forward(QueuedInputEvent q) {
onDeliverToNext(q);
}
/**
* Applies a result code from {@link #onProcess} to the specified event.
*/
protected void apply(QueuedInputEvent q, int result) {
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);
}
}
/**
* Called when an event is ready to be processed.
* @return A result code indicating how the event was handled.
*/
protected int onProcess(QueuedInputEvent q) {
return FORWARD;
}
/**
* Called when an event is being delivered to the next stage.
*/
protected void onDeliverToNext(QueuedInputEvent q) {
if (mNext != null) {
mNext.deliver(q);
} else {