Android输入法显示方式大概分为两种:用户手动点击输入框和应用程序设置了输入法自动显示
本文基于Android9.x来分析
目录
1 :viewClicked流程
1.1 viewClicked
1.2 checkFocus
1.3 startInputInner
1.4 startInputOrWindowGainedFocus
1.5 startInputLocked
1.6 startInputUncheckedLocked
1.7 attachNewInputLocked
1.7.1 处理返回的结果
2:showSoftInput流程
2.1 showSoftInput
2.2 IMMS#showSoftInput
2.3 showCurrentInputLocked
2.4 IMS$InputMethodImpl$showSoftInput
2.5 dispatchOnShowInputRequested
2.6 IMS$showWindow
2.7 showWindowInner
2:从用户点击输入框开始
EditText本身是TextView的子类,触摸事件的起点在TextView的onTouchEvent方法中
if (touchIsFinished && (isTextEditable() || textIsSelectable)) {
// Show the IME, except when selecting in read-only text.
final InputMethodManager imm = InputMethodManager.peekInstance();
viewClicked(imm);
if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
imm.showSoftInput(this, 0);
}
// The above condition ensures that the mEditor is not null
mEditor.onTouchUpEvent(event);
handled = true;
}
2.1:viewClicked
protected void viewClicked(InputMethodManager imm) {
if (imm != null) {
imm.viewClicked(this);
}
}
2:2:InputMethodManager::viewClicked
public void viewClicked(View view) {
final boolean focusChanged = mServedView != mNextServedView;
checkFocus();
synchronized (mH) {
if ((mServedView != view && (mServedView == null
|| !mServedView.checkInputConnectionProxy(view)))
|| mCurrentTextBoxAttribute == null || mCurMethod == null) {
return;
}
try {
if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged);
mCurMethod.viewClicked(focusChanged);
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
}
}
mCurMethod代表的是一个binder代理对象,对应的是
IInputMethodSessionWrapper
2.3:IInputMethodSessionWrapper::viewClicked
@Override
public void viewClicked(boolean focusChanged) {
mCaller.executeOrSendMessage(
mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0));
}
case DO_VIEW_CLICKED: {
mInputMethodSession.viewClicked(msg.arg1 == 1);
return;
}
InputMethodSession对应的是一个接口,其实现类为AbstractInputMethodSessionImpl,AbstractInputMethodSessionImpl本身是一个抽象类,InputMethodSessionImpl有继承它,所以最终调用的是InputMethodSessionImpl的viewClicked
2.4::InputMethodSessionImpl::viewClicked
public void viewClicked(boolean focusChanged) {
if (!isEnabled()) {
return;
}
InputMethodService.this.onViewClicked(focusChanged);
}
2.5:InputMethodService::onViewClicked
public void onViewClicked(boolean focusChanged) {
// Intentionally empty
}
2.6:TextView::checkFocus
public void checkFocus() {
if (checkFocusNoStartInput(false)) {
startInputInner(InputMethodClient.START_INPUT_REASON_CHECK_FOCUS, null, 0, 0, 0);
}
}
2.7:startInputInner
这个方法主要是和输入法服务建立连接以及绑定
try {
if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
+ ic + " tba=" + tba + " controlFlags=#"
+ Integer.toHexString(controlFlags));
final InputBindResult res = mService.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, controlFlags, softInputMode,
windowFlags, tba, servedContext, missingMethodFlags,
view.getContext().getApplicationInfo().targetSdkVersion);
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res == null) {
Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
+ " null. startInputReason="
+ InputMethodClient.getStartInputReason(startInputReason)
+ " editorInfo=" + tba
+ " controlFlags=#" + Integer.toHexString(controlFlags));
return false;
}
if (res.id != null) {
setInputChannelLocked(res.channel);
mBindSequence = res.sequence;
mCurMethod = res.method;
mCurId = res.id;
mNextUserActionNotificationSequenceNumber =
res.userActionNotificationSequenceNumber;
} else if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
}
switch (res.result) {
case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
mRestartOnNextWindowFocus = true;
break;
}
if (mCurMethod != null && mCompletions != null) {
try {
mCurMethod.displayCompletions(mCompletions);
} catch (RemoteException e) {
}
}
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
mService表示的是Imms在客户端的代理对象
2.8:IMMS::startInputOrWindowGainedFocus
final InputBindResult result;
if (windowToken != null) {
result = windowGainedFocus(startInputReason, client, windowToken, controlFlags,
softInputMode, windowFlags, attribute, inputContext, missingMethods,
unverifiedTargetSdkVersion);
} else {
result = startInput(startInputReason, client, inputContext, missingMethods, attribute,
controlFlags);
}
if (result == null) {
// This must never happen, but just in case.
Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
+ InputMethodClient.getStartInputReason(startInputReason)
+ " windowFlags=#" + Integer.toHexString(windowFlags)
+ " editorInfo=" + attribute);
return InputBindResult.NULL;
}
windowToken这个地方不为null,代码逻辑执行的是windowToken
2.9:windowGainedFocus
ClientState cs = mClients.get(client.asBinder());
//在窗口添加的时候会创建一个会话session,在会话创建的时候添加的
// if (mService.mInputMethodManager != null) {
// mService.mInputMethodManager.addClient(client, inputContext,
// mUid, mPid);
//Session.java的构造方法中
} else {
client.setUsingInputMethod(false);
}
if (cs == null) {----->
throw new IllegalArgumentException("unknown client "
+ client.asBinder());
}
try {
if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
} catch (RemoteException e) {
}
if (!calledFromValidUser) {
hideCurrentInputLocked(0, null);
return InputBindResult.INVALID_USER;
}
if (mCurFocusedWindow == windowToken) {
if (attribute != null) {
return startInputUncheckedLocked(cs, inputContext, missingMethods,
attribute, controlFlags, startInputReason);
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
null, null, null, -1, -1);
}
2.10:startInputUncheckedLocked
if (mCurId != null && mCurId.equals(mCurMethodId)) {
if (cs.curSession != null) {
return attachNewInputLocked(startInputReason,
(controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0);
}
if (mHaveConnection) {
if (mCurMethod != null) {
requestClientSessionLocked(cs);
return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
null, null, mCurId, mCurSeq,
mCurUserActionNotificationSequenceNumber);
} else if (SystemClock.uptimeMillis()
< (mLastBindTime+TIME_TO_RECONNECT)) {
return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
null, null, mCurId, mCurSeq,
mCurUserActionNotificationSequenceNumber);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
}
}
}
return startInputInnerLocked();
2.11:attachNewInputLocked
final SessionState session = mCurClient.curSession;
executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
MSG_START_INPUT, mCurInputContextMissingMethods, initial ? 0 : 1 /* restarting */,
startInputToken, session, mCurInputContext, mCurAttribute));
if (mShowRequested) {----------->此时mShowRequested为false
showCurrentInputLocked(getAppShowFlags(), null);
}
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
第一部分主要是应用程序app和输入法服务IMMS建立连接
3:应用程序请求输入法显示
if (touchIsFinished && (isTextEditable() || textIsSelectable)) {
// Show the IME, except when selecting in read-only text.
final InputMethodManager imm = InputMethodManager.peekInstance();
viewClicked(imm);
if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
imm.showSoftInput(this, 0);
}
// The above condition ensures that the mEditor is not null
mEditor.onTouchUpEvent(event);
handled = true;
}
3.1:showSoftInput
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
checkFocus();
synchronized (mH) {
if (mServedView != view && (mServedView == null
|| !mServedView.checkInputConnectionProxy(view))) {
return false;
}
try {
return mService.showSoftInput(mClient, flags, resultReceiver);---------->请求输入法服务来显示showSoftInput
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
3:2:IMMS::showSoftInput
try {
synchronized (mMethodMap) {
if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) {
try {
if (!mIWindowManager.inputMethodClientHasFocus(client)) {
return false;
}
} catch (RemoteException e) {
return false;
}
}
return showCurrentInputLocked(flags, resultReceiver);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
3:3:showCurrentInputLocked
boolean res = false;
if (mCurMethod != null) {
executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod,
resultReceiver));
mInputShown = true;
if (mHaveConnection && !mVisibleBound) {
bindCurrentInputMethodServiceLocked(
mCurIntent, mVisibleConnection, IME_VISIBLE_BIND_FLAGS);
mVisibleBound = true;
}
res = true;
} else if (mHaveConnection && SystemClock.uptimeMillis()
>= (mLastBindTime+TIME_TO_RECONNECT)) {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, mCurMethodId,
SystemClock.uptimeMillis()-mLastBindTime,1);
Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()");
mContext.unbindService(this);
bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS);
} else {
}
3.4::MSG_SHOW_SOFT_INPUT消息处理
case MSG_SHOW_SOFT_INPUT:
args = (SomeArgs)msg.obj;
try {
if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
+ msg.arg1 + ", " + args.arg2 + ")");
((IInputMethod)args.arg1).showSoftInput(msg.arg1, (ResultReceiver)args.arg2);
} catch (RemoteException e) {
}
args.recycle();
return true;
((IInputMethod)args.arg1)代表的是arg2
public Message obtainMessageIOO(int what, int arg1, Object arg2, Object arg3) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = arg2;
args.arg2 = arg3;
return mH.obtainMessage(what, arg1, 0, args);
}
arg2代表是IInputMethod mCurMethod;是一个binder代理对象,表示的是输入法在IMMS的代理对象,通过这个对象我们可以访问输入法进程里面的方法
3.5:IInputMethodWrapper::showSoftInput
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT,
flags, resultReceiver));
}
//最终调用的是inputMethod的showSoftInput,inputMethod是InputMethod,InputMethod是一个接口,需要找到其实现类
case DO_SHOW_SOFT_INPUT:
inputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
return;
3.6:InputMethodServic.InputMethodImpl.java
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
boolean wasVis = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
try {
showWindow(true);------>显示输入法窗口
} catch (BadTokenException e) {
}
}
clearInsetOfPreviousIme();
mImm.setImeWindowStatus(mToken, mStartInputToken,
mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
if (resultReceiver != null) {
resultReceiver.send(wasVis != isInputViewShown()
? InputMethodManager.RESULT_SHOWN
: (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
}
}
InputMethodService是输入法服务,工作在输入法应用的进程中
3.7:showWindow
try {
mWindowWasVisible = mWindowVisible;
mInShowWindow = true;
showWindowInner(showInput);
} catch (BadTokenException e) {
mWindowVisible = false;
mWindowAdded = false;
throw e;
} finally {
// TODO: Is it OK to set true when we get BadTokenException?
mWindowWasVisible = true;
mInShowWindow = false;
}
3.8:showWindowInner
void showWindowInner(boolean showInput) {
boolean doShowInput = false;
final int previousImeWindowStatus =
(mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
mWindowVisible = true;
if (!mShowInputRequested && mInputStarted && showInput) {
doShowInput = true;
mShowInputRequested = true;
}
initialize();
updateFullscreenMode();
updateInputViewShown();
if (!mWindowAdded || !mWindowCreated) {
mWindowAdded = true;
mWindowCreated = true;
initialize();
View v = onCreateCandidatesView();
if (v != null) {
setCandidatesView(v);
}
}
if (mShowInputRequested) {
if (!mInputViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
mInputViewStarted = true;
onStartInputView(mInputEditorInfo, false);
}
} else if (!mCandidatesViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
mCandidatesViewStarted = true;
onStartCandidatesView(mInputEditorInfo, false);
}
if (doShowInput) {
startExtractingText(false);
}
final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
if (previousImeWindowStatus != nextImeWindowStatus) {
mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
mBackDisposition);
}
if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
onWindowShown();
mWindow.show();---->输入法窗口是一个对话框
mShouldClearInsetOfPreviousIme = false;
}
}
最终输入法窗口被显示出来