android 输入法如何启动流程_Android输入法显示流程
Android輸入法顯示方式大概分為兩種:用戶手動(dòng)點(diǎn)擊輸入框和應(yīng)用程序設(shè)置了輸入法自動(dòng)顯示
本文基于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 處理返回的結(jié)果
2:showSoftInput流程
2.1 showSoftInput
2.2 IMMS#showSoftInput
2.3 showCurrentInputLocked
2.4 IMSInputMethodImplInputMethodImplInputMethodImplshowSoftInput
2.5 dispatchOnShowInputRequested
2.6 IMS$showWindow
2.7 showWindowInner
2:從用戶點(diǎn)擊輸入框開始
EditText本身是TextView的子類,觸摸事件的起點(diǎn)在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代表的是一個(gè)binder代理對(duì)象,對(duì)應(yīng)的是
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對(duì)應(yīng)的是一個(gè)接口,其實(shí)現(xiàn)類為AbstractInputMethodSessionImpl,AbstractInputMethodSessionImpl本身是一個(gè)抽象類,InputMethodSessionImpl有繼承它,所以最終調(diào)用的是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
這個(gè)方法主要是和輸入法服務(wù)建立連接以及綁定
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在客戶端的代理對(duì)象
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這個(gè)地方不為null,代碼邏輯執(zhí)行的是windowToken
2.9:windowGainedFocus
ClientState cs = mClients.get(client.asBinder());
//在窗口添加的時(shí)候會(huì)創(chuàng)建一個(gè)會(huì)話session,在會(huì)話創(chuàng)建的時(shí)候添加的
// if (mService.mInputMethodManager != null) {
// mService.mInputMethodManager.addClient(client, inputContext,
// mUid, mPid);
//Session.java的構(gòu)造方法中
} 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) {----------->此時(shí)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);
第一部分主要是應(yīng)用程序app和輸入法服務(wù)IMMS建立連接
3:應(yīng)用程序請(qǐng)求輸入法顯示
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);---------->請(qǐng)求輸入法服務(wù)來顯示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;是一個(gè)binder代理對(duì)象,表示的是輸入法在IMMS的代理對(duì)象,通過這個(gè)對(duì)象我們可以訪問輸入法進(jìn)程里面的方法
3.5:IInputMethodWrapper::showSoftInput
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT,
flags, resultReceiver));
}
//最終調(diào)用的是inputMethod的showSoftInput,inputMethod是InputMethod,InputMethod是一個(gè)接口,需要找到其實(shí)現(xiàn)類
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是輸入法服務(wù),工作在輸入法應(yīng)用的進(jìn)程中
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();---->輸入法窗口是一個(gè)對(duì)話框
mShouldClearInsetOfPreviousIme = false;
}
}
總結(jié)
以上是生活随笔為你收集整理的android 输入法如何启动流程_Android输入法显示流程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年中国InGaAs+APD接收机
- 下一篇: CorelDRAW X3中文版服装创意设