zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

android 输入法如何启动流程_Android输入法显示流程

Android流程 如何 启动 显示 输入法
2023-09-14 09:13:52 时间

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 I n p u t M e t h o d I m p l InputMethodImpl InputMethodImplshowSoftInput

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;

}

}