在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 {
finishInputEvent(q);
}
}
protected boolean shouldDropInputEvent(QueuedInputEvent q) {
if (mView == null || !mAdded) {
Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent);
return true;
} else if (!mAttachInfo.mHasWindowFocus &&
!q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) &&
!isTerminalInputEvent(q.mEvent)) {
// If this is a focused event and the window doesn't currently have input focus,
// then drop this event. This could be an event that came back from the previous
// stage but the window has lost focus in the meantime.
Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
return true;
}
return false;
}
void dump(String prefix, PrintWriter writer) {
if (mNext != null) {
mNext.dump(prefix, writer);
}
}
}
mLastWasImTarget表示这个View是能和输入法交互的,比如是TextView、EditText就可以,如果没法跟输入法交互肯定也就没必要往下处理了。isInLocalFocusMode表示设置了flag FLAG_LOCAL_FOCUS_MODE,默认false。这里会把QueuedInputEvent交给InputMethodManager处理,熟悉的方法名dispatchInputEvent,这里维测也有一行关键日志。
ViewRootImpl::ImeInputStage
/**
* Delivers input events to the ime.
* Does not support pointer events.
*/
final class ImeInputStage extends AsyncInputStage
implements InputMethodManager.FinishedInputEventCallback {
public ImeInputStage(InputStage next, String traceCounter) {
super(next, traceCounter);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
final InputEvent event = q.mEvent;
if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
int result = imm.dispatchInputEvent(event, q, this, mHandler);
if (result == InputMethodManager.DISPATCH_HANDLED) {
return FINISH_HANDLED;
} else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
return FINISH_NOT_HANDLED;
} else {
return DEFER; // callback will be invoked later
}
}
}
return FORWARD;
}
@Override
public void onFinishedInputEvent(Object token, boolean handled) {
QueuedInputEvent q = (QueuedInputEvent)token;
if (handled) {
finish(q, true);
return;
}
forward(q);
}
}
二、InputMethodManager如何将事件分发给输入法InputMethodService
这一段重点看IMM是如何通过InputChannel跟IME通信的。
InputMethodManager::dispatchInputEvent
/**
* Dispatches an input event to the IME.
*
* Returns {@link #DISPATCH_HANDLED} if the event was handled.
* Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
* Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
* callback will be invoked later.
*
* @hide
*/
public int dispatchInputEvent(InputEvent event, Object token,
FinishedInputEventCallback callback, Handler handler) {
synchronized (mH) {
if (mCurMethod != null) {
if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent)event;
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
&& keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
&& keyEvent.getRepeatCount() == 0) {
showInputMethodPickerLocked();
return DISPATCH_HANDLED;
}
}
if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
PendingEvent p = obtainPendingEventLocked(
event, token, mCurId, callback, handler);
if (mMainLooper.isCurrentThread()) {
// Already running on the IMM thread so we can send the event immediately.
return sendInputEventOnMainLooperLocked(p);
}
// Post the event to the IMM thread.
Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
msg.setAsynchronous(true);
mH.sendMessage(msg);
return DISPATCH_IN_PROGRESS;
}
}
return DISPATCH_NOT_HANDLED;
}
可以看到实际上是通过ImeInputEventSender在主线程上通过mCurChannel发送Input事件的。
IMM:sendInputEventOnMainLooperLocked
// Must be called on the main looper
int sendInputEventOnMainLooperLocked(PendingEvent p) {
if (mCurChannel != null) {
if (mCurSender == null) {
mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
}
final InputEvent event = p.mEvent;
final int seq = event.getSequenceNumber();
if (mCurSender.sendInputEvent(seq, event)) {
mPendingEvents.put(seq, p);
Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
mPendingEvents.size());
Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
msg.setAsynchronous(true);
mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
return DISPATCH_IN_PROGRESS;
}
Log.w(TAG, "Unable to send input event to IME: "
+ mCurId + " dropping: " + event);
}
return DISPATCH_NOT_HANDLED;
}
ImeInputEventSender继承于InputEventSender,正如类名一样,这是专门负责发送Input事件的类,通信方式是socket,基于一对InputChannel,这和Input框架是一样的。那么这对InputChannel是从哪来的呢?后面会谈到。
InputEventSender::sendInputEvent
public final boolean sendInputEvent(int seq, InputEvent event) {
if (event == null) {
throw new IllegalArgumentException("event must not be null");
}
if (mSenderPtr == 0) {
Log.w(TAG, "Attempted to send an input event but the input event "
+ "sender has already been disposed.");
return false;
}
if (event instanceof KeyEvent) {
return nativeSendKeyEvent(mSenderPtr, seq, (KeyEvent)event);
} else {
return nativeSendMotionEvent(mSenderPtr, seq, (MotionEvent)event);
}
}
InputPublisher发布KeyEvent,在对端的Looper接收到对应InputChannel fd的事件后会回调对应的InputEventReceiver来处理,和开篇是完全一样的。
android_view_inputeventsender::init
NativeInputEventSender::NativeInputEventSender(JNIEnv* env,
jobject senderWeak, const sp& inputChannel,
const sp& messageQueue) :
mSenderWeakGlobal(env->NewGlobalRef(senderWeak)),
mInputPublisher(inputChannel), mMessageQueue(messageQueue),
mNextPublishedSeq(1) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName());
#endif
}
android_view_inputeventsender::sendKeyEvent
status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName(), seq);
#endif
uint32_t publishedSeq = mNextPublishedSeq++;
status_t status = mInputPublisher.publishKeyEvent(publishedSeq,
event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
event->getKeyCode(), event->getScanCode(), event->getMetaState(),
event->getRepeatCount(), event->getDownTime(), event->getEventTime());
if (status) {
ALOGW("Failed to send key event on channel '%s'. status=%d",
getInputChannelName(), status);
return status;
}
mPublishedSeqMap.add(publishedSeq, seq);
return OK;
}
mInputChannel->sendMessage
InputTransport::InputPublisher::publishKeyEvent
status_t InputPublisher::publishKeyEvent(
uint32_t seq,
int32_t deviceId,
int32_t source,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
"downTime=%lld, eventTime=%lld",
mChannel->getName().string(), seq,
deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
downTime, eventTime);
#endif
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
return BAD_VALUE;
}
InputMessage msg;
msg.header.type = InputMessage::TYPE_KEY;
msg.body.key.seq = seq;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
msg.body.key.scanCode = scanCode;
msg.body.key.metaState = metaState;
msg.body.key.repeatCount = repeatCount;
msg.body.key.downTime = downTime;
msg.body.key.eventTime = eventTime;
return mChannel->sendMessage(&msg);
}
在IMM和IME的会话创建包装类IINputMethodSessionWrapper创建时,会将会话类InputMethodSession和客户端InputChannel一同保存下来,还会基于这个channel在主线程创建一个InputEventReceiver的实现类对象ImeInputEventReceiver。
public IInputMethodSessionWrapper(Context context,
InputMethodSession inputMethodSession, InputChannel channel) {
mCaller = new HandlerCaller(context, null,
this, true /*asyncHandler*/);
mInputMethodSession = inputMethodSession;
mChannel = channel;
if (channel != null) {
mReceiver = new ImeInputEventReceiver(channel, context.getMainLooper());
}
}
InputEventReceiver::init
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
按照顺序依次处理,将inputChannel的fd添加到Looper关注的队列里面,当有事件来临的时候会回调handleEvent函数,经InputConsumer处理最终会调到Java侧的InputEventReceiver的dispatchInputEvent方法。具体就不一一分析了,这里面和Input框架是一样的。
android_view_InputEventReceiver::nativeInit
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
sp inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
}
sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
sp receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
if (status) {
String8 message;
message.appendFormat("Failed to initialize input event receiver. status=%d", status);
jniThrowRuntimeException(env, message.string());
return 0;
}
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast(receiver.get());
}
android_view_InputEventReceiver::NativeInputEventReceiver::initialize
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
这里和native侧的Looper轮询机制有关
android_view_InputEventReceiver::NativeInputEventReceiver::setFdEvents
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
当有新事件来临时,looper会监控到会调用该fd对应的回调方法,也就是handleEvent
android_view_InputEventReceiver::NativeInputEventReceiver::handleEvent
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data)
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch)
这是regJNI时注册的java侧的类和方法,其中就是InputEventReceiver的dispatchInputEvent
int register_android_view_InputEventReceiver(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver",
gMethods, NELEM(gMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver");
GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent,
gInputEventReceiverClassInfo.clazz,
"dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchBatchedInputEventPending,
gInputEventReceiverClassInfo.clazz,
"dispatchBatchedInputEventPending", "()V");
return 0;
}
在consumeEvent最后就是调用java的InputEventReceiver来进行分发。input事件分发的基础就是native的Looper和一对InputChannel,Looper通过EPOLL机制监听InputChannel socket通道的fd。
if (inputEventObj) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
#endif
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching input event.");
skipCallbacks = true;
}
env->DeleteLocalRef(inputEventObj);
} else {
ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
skipCallbacks = true;
}
下面再回到Java侧。
InputEventReceiver::dispatchInputEvent
// Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
这是InputMethodSession的封装类IMethodSessionWrapper在初始化时创建的ImeInputEventReceiver对象。
IMethodSessionWrapper::ImeInputEventReceiver
private final class ImeInputEventReceiver extends InputEventReceiver
implements InputMethodSession.EventCallback {
private final SparseArray mPendingEvents = new SparseArray();
public ImeInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
if (mInputMethodSession == null) {
// The session has been finished.
finishInputEvent(event, false);
return;
}
final int seq = event.getSequenceNumber();
mPendingEvents.put(seq, event);
if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent)event;
mInputMethodSession.dispatchKeyEvent(seq, keyEvent, this);
} else {
MotionEvent motionEvent = (MotionEvent)event;
if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
mInputMethodSession.dispatchTrackballEvent(seq, motionEvent, this);
} else {
mInputMethodSession.dispatchGenericMotionEvent(seq, motionEvent, this);
}
}
}
@Override
public void finishedEvent(int seq, boolean handled) {
int index = mPendingEvents.indexOfKey(seq);
if (index >= 0) {
InputEvent event = mPendingEvents.valueAt(index);
mPendingEvents.removeAt(index);
finishInputEvent(event, handled);
}
}
}
IInputMethodSessionWrapper是在startInput启动输入法时IMMS 绑定到IMS时createSession时实例的,等会再看。
public IInputMethodSessionWrapper(Context context,
InputMethodSession inputMethodSession, InputChannel channel) {
mCaller = new HandlerCaller(context, null,
this, true /*asyncHandler*/);
mInputMethodSession = inputMethodSession;
mChannel = channel;
if (channel != null) {
mReceiver = new ImeInputEventReceiver(channel, context.getMainLooper());
}
}
AbstractInputMethodService内部有AbstractInputMethodImpl和AbstractInputMethodSessionImpl两个抽象类,前者用来创建和管理会话session,后者则是用来分发Input事件。
处理之后还要再走一遍socket通信告诉应用server端这个Input事件处理结束,应用server端作为client端再通知system_server input事件处理结束。
AbstractInputMethodSessionImpl::dispatchKeyEvent
/**
* Take care of dispatching incoming key events to the appropriate
* callbacks on the service, and tell the client when this is done.
*/
@Override
public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
boolean handled = event.dispatch(AbstractInputMethodService.this,
mDispatcherState, this);
if (callback != null) {
callback.finishedEvent(seq, handled);
}
}
KeyEvent::dispatch
public final boolean dispatch(Callback receiver, DispatcherState state,
Object target) {
switch (mAction) {
case ACTION_DOWN: {
mFlags &= ~FLAG_START_TRACKING;
if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
+ ": " + this);
boolean res = receiver.onKeyDown(mKeyCode, this);
if (state != null) {
if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
if (DEBUG) Log.v(TAG, " Start tracking!");
state.startTracking(this, target);
} else if (isLongPress() && state.isTracking(this)) {
try {
if (receiver.onKeyLongPress(mKeyCode, this)) {
if (DEBUG) Log.v(TAG, " Clear from long press!");
state.performedLongPress(this);
res = true;
}
} catch (AbstractMethodError e) {
}
}
}
return res;
}
下面就是针对这个Key事件进行处理了,上下左右等等,下面就不再看了。
InputMethodService::onKeyDown
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (handleBack(false)) {
event.startTracking();
return true;
}
return false;
}
return doMovementKey(keyCode, event, MOVEMENT_DOWN);
}
现在看看InputMethodSession创建的过程,也就是IMM和IMS如何拿到一对InputChannel的。
在IMMS去绑定IMS时,在完成绑定后会回调IMMS::onServiceConnected方法。这时候会把InputMethodService的客户端保存下来,并且会把之前创建的Binder token通过attachToken回调最终会调用到InputMethodService::InputMethodImpl::attchToken,将它设置为自己Window的token,以后方便wms管理。最后则会调用requestClientSessionLocked请求为客户端mCurClient创建InputMethodSession。
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mMethodMap) {
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
mCurMethod = IInputMethod.Stub.asInterface(service);
if (mCurToken == null) {
Slog.w(TAG, "Service connected without a token!");
unbindCurrentMethodLocked(false, false);
return;
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
requestClientSessionLocked(mCurClient);
}
}
}
}
这里会创建一堆InputChannel,server端channels[0]最终会再MethodCallback的回调方法sessionCreated保存到IMMS里面去创建InputEventSender,client端channels[1]则会随着IInputMethod的createSession的动作在输入法侧创建InputEventReceiver。
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {
if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
cs.sessionRequested = true;
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(
MSG_CREATE_SESSION, mCurMethod, channels[1],
new MethodCallback(this, mCurMethod, channels[0])));
}
}
case MSG_CREATE_SESSION: {
args = (SomeArgs)msg.obj;
IInputMethod method = (IInputMethod)args.arg1;
InputChannel channel = (InputChannel)args.arg2;
try {
method.createSession(channel, (IInputSessionCallback)args.arg3);
} catch (RemoteException e) {
} finally {
// Dispose the channel if the input method is not local to this process
// because the remote proxy will get its own copy when unparceled.
if (channel != null && Binder.isProxy(method)) {
channel.dispose();
}
}
args.recycle();
return true;
}
输入法侧createSession时创建InputMethodSessionCallbackWrapper,后续会回调它的sessionCreated方法,再创建IInputMethodSessionWrapper就回到了本文前面分析过的地方,这里会创建InputEventReceiver。
IInputMethodWrapper
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mCaller.mContext, (InputChannel)args.arg1,
(IInputSessionCallback)args.arg2));
args.recycle();
return;
}
IInputMethodSessionCallbackWrapper
static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
final Context mContext;
final InputChannel mChannel;
final IInputSessionCallback mCb;
InputMethodSessionCallbackWrapper(Context context, InputChannel channel,
IInputSessionCallback cb) {
mContext = context;
mChannel = channel;
mCb = cb;
}
@Override
public void sessionCreated(InputMethodSession session) {
try {
if (session != null) {
IInputMethodSessionWrapper wrap =
new IInputMethodSessionWrapper(mContext, session, mChannel);
mCb.sessionCreated(wrap);
} else {
if (mChannel != null) {
mChannel.dispose();
}
mCb.sessionCreated(null);
}
} catch (RemoteException e) {
}
}
}
IInputMethodSessionWrapper
public IInputMethodSessionWrapper(Context context,
InputMethodSession inputMethodSession, InputChannel channel) {
mCaller = new HandlerCaller(context, null,
this, true /*asyncHandler*/);
mInputMethodSession = inputMethodSession;
mChannel = channel;
if (channel != null) {
mReceiver = new ImeInputEventReceiver(channel, context.getMainLooper());
}
}
再看server端
private static final class MethodCallback extends IInputSessionCallback.Stub {
private final InputMethodManagerService mParentIMMS;
private final IInputMethod mMethod;
private final InputChannel mChannel;
MethodCallback(InputMethodManagerService imms, IInputMethod method,
InputChannel channel) {
mParentIMMS = imms;
mMethod = method;
mChannel = channel;
}
@Override
public void sessionCreated(IInputMethodSession session) {
mParentIMMS.onSessionCreated(mMethod, session, mChannel);
}
}
IMMS在session创建之后会将IInputClient和InputMethod绑定起来,InputBindResult实现了Parcelable接口,是可以跨进程通信的。
public InputBindResult(IInputMethodSession _method, InputChannel _channel,
String _id, int _sequence) {
method = _method;
channel = _channel;
id = _id;
sequence = _sequence;
}
void onSessionCreated(IInputMethod method, IInputMethodSession session,
InputChannel channel) {
synchronized (mMethodMap) {
if (mCurMethod != null && method != null
&& mCurMethod.asBinder() == method.asBinder()) {
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
mCurClient.curSession = new SessionState(mCurClient,
method, session, channel);
InputBindResult res = attachNewInputLocked(true);
if (res.method != null) {
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
MSG_BIND_METHOD, mCurClient.client, res));
}
return;
}
}
}
// Session abandoned. Close its associated input channel.
channel.dispose();
}
InputBindResult attachNewInputLocked(boolean initial) {
if (!mBoundToMethod) {
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
mBoundToMethod = true;
}
final SessionState session = mCurClient.curSession;
if (initial) {
executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
MSG_START_INPUT, session, mCurInputContext, mCurAttribute));
} else {
executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute));
}
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
}
return new InputBindResult(session.session,
session.channel != null ? session.channel.dup() : null, mCurId, mCurSeq);
}
case MSG_BIND_METHOD: {
args = (SomeArgs)msg.obj;
IInputMethodClient client = (IInputMethodClient)args.arg1;
InputBindResult res = (InputBindResult)args.arg2;
try {
client.onBindMethod(res);
} catch (RemoteException e) {
Slog.w(TAG, "Client died receiving input method " + args.arg2);
} finally {
// Dispose the channel if the input method is not local to this process
// because the remote proxy will get its own copy when unparceled.
if (res.channel != null && Binder.isProxy(client)) {
res.channel.dispose();
}
}
args.recycle();
return true;
}
IINputMethodClient是InputMethodManager创建的客户端对象。
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
// No need to check for dump permission, since we only give this
// interface to the system.
CountDownLatch latch = new CountDownLatch(1);
SomeArgs sargs = SomeArgs.obtain();
sargs.arg1 = fd;
sargs.arg2 = fout;
sargs.arg3 = args;
sargs.arg4 = latch;
mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
try {
if (!latch.await(5, TimeUnit.SECONDS)) {
fout.println("Timeout waiting for dump");
}
} catch (InterruptedException e) {
fout.println("Interrupted waiting for dump");
}
}
@Override
public void setUsingInputMethod(boolean state) {
}
@Override
public void onBindMethod(InputBindResult res) {
mH.sendMessage(mH.obtainMessage(MSG_BIND, res));
}
@Override
public void onUnbindMethod(int sequence) {
mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0));
}
@Override
public void setActive(boolean active) {
mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
}
};
终于到最后了
case MSG_BIND: {
final InputBindResult res = (InputBindResult)msg.obj;
if (DEBUG) {
Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);
}
synchronized (mH) {
if (mBindSequence < 0 || mBindSequence != res.sequence) {
Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
+ ", given seq=" + res.sequence);
if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
}
return;
}
setInputChannelLocked(res.channel);
mCurMethod = res.method;
mCurId = res.id;
mBindSequence = res.sequence;
}
startInputInner(null, 0, 0, 0);
return;
}
这里会把InputChannel保存下来
void setInputChannelLocked(InputChannel channel) {
if (mCurChannel != channel) {
if (mCurSender != null) {
flushPendingEventsLocked();
mCurSender.dispose();
mCurSender = null;
}
if (mCurChannel != null) {
mCurChannel.dispose();
}
mCurChannel = channel;
}
}
那什么时候会创建InputEventSender,那就是有Input事件来临的时候,会拿server端的InputChannel在主线程上创建InputInputSender。
int sendInputEventOnMainLooperLocked(PendingEvent p) {
if (mCurChannel != null) {
if (mCurSender == null) {
mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
}
final InputEvent event = p.mEvent;
final int seq = event.getSequenceNumber();
if (mCurSender.sendInputEvent(seq, event)) {
mPendingEvents.put(seq, p);
Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
mPendingEvents.size());
Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
msg.setAsynchronous(true);
mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
return DISPATCH_IN_PROGRESS;
}
Log.w(TAG, "Unable to send input event to IME: "
+ mCurId + " dropping: " + event);
}
return DISPATCH_NOT_HANDLED;
}
over。