1、继续于InputMethodService的输入法服务类,需要提交输入法的输入的内容,如下:
Log.d(TAG, "Commit Result Text=" + resultText);
InputConnection ic = getCurrentInputConnection();
if (null != ic)
{
boolean isCommit = ic.commitText(resultText, 1);
Log.d(TAG, "Commit Result Text=isCommit=" + isCommit);
}
跟踪此InputConnection怎么获取的,如下:
/**
* Retrieve the currently active InputConnection that is bound to
* the input method, or null if there is none.
*/
public InputConnection getCurrentInputConnection() {
InputConnection ic = mStartedInputConnection;
if (ic != null) {
return ic;
}
return mInputConnection;
}
void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
if (!restarting) {
doFinishInput();
}
mInputStarted = true;
mStartedInputConnection = ic;
从这里我们看到是doStartInput方法传入的,这里特别需要注意getCurrentInputConnection,如果出错,则会把输入法的内容写入了上一次界面的编辑框,而当前的编辑框什么都没有显示的情况,这种情况有可能发生,这里不再拓展
继续跟踪上面的被调用,如下:
public void startInput(InputConnection ic, EditorInfo attribute) {
if (DEBUG) Log.v(TAG,"InputMethodService class" + "startInput(): editor=" + attribute);
doStartInput(ic, attribute, false);
}
在InputMethodManagerService.java下面:
case MSG_START_INPUT:
args = (HandlerCaller.SomeArgs)msg.obj;
try {
SessionState session = (SessionState)args.arg1;
setEnabledSessionInMainThread(session);
session.method.startInput((IInputContext)args.arg2,
(EditorInfo)args.arg3);
} catch (RemoteException e) {
}
这里的MSG_START_INPUT消息继续跟踪在哪被调用:
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, "InputMethodManagerService class" + "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
}
return new InputBindResult(session.session, mCurId, mCurSeq);
从上面来看指向了mCurInputContext,下面是它的定义:
/**
* The input context last provided by the current client.
*/
IInputContext mCurInputContext;
这个值又是从哪里被赋值的呢?
InputBindResult startInputUncheckedLocked(ClientState cs,
IInputContext inputContext, EditorInfo attribute, int controlFlags) {
// If no method is currently selected, do nothing.
if (mCurMethodId == null) {
return mNoBinding;
}
if (mCurClient != cs) {
// If the client is changing, we need to switch over to the new
// one.
unbindCurrentClientLocked();
if (DEBUG) Slog.v(TAG, "InputMethodManagerService class" + "switching to client: client = "
+ cs.client.asBinder());
// If the screen is on, inform the new client it is active
if (mScreenOn) {
try {
cs.client.setActive(mScreenOn);
} catch (RemoteException e) {
Slog.w(TAG, "InputMethodManagerService class" + "Got RemoteException sending setActive notification to pid "
+ cs.pid + " uid " + cs.uid);
}
}
}
// Bump up the sequence for this client and attach it.
mCurSeq++;
if (mCurSeq <= 0) mCurSeq = 1;
mCurClient = cs;
mCurInputContext = inputContext;
看到上面的最后一行,即从startInputUncheckedLocked方法被传入的,继续跟踪这个方法在哪被调用
InputBindResult startInputLocked(IInputMethodClient client,
IInputContext inputContext, EditorInfo attribute, int controlFlags) {
@Override
public InputBindResult startInput(IInputMethodClient client,
IInputContext inputContext, EditorInfo attribute, int controlFlags) {
synchronized (mMethodMap) {
final long ident = Binder.clearCallingIdentity();
try {
return startInputLocked(client, inputContext, attribute, controlFlags);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
一直跟踪到InputMethodManager.java的startInputInner方法中,如下:
mServedInputConnection = ic; IInputContext servedContext;
if (ic != null) {
mCursorSelStart = tba.initialSelStart;
mCursorSelEnd = tba.initialSelEnd;
mCursorCandStart = -1;
mCursorCandEnd = -1;
mCursorRect.setEmpty();
servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);
} else {
servedContext = null;
}
try {
if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
+ ic + " tba=" + tba + " controlFlags=#"
+ Integer.toHexString(controlFlags));
InputBindResult res;
if (windowGainingFocus != null) {
res = mService.windowGainedFocus(mClient, windowGainingFocus,
controlFlags, softInputMode, windowFlags,
tba, servedContext);
} else {
res = mService.startInput(mClient,
servedContext, tba, controlFlags);
}
从上面的代码来看,我们需要跟踪mServedInputConnection,它的定义:
/**
* The InputConnection that was last retrieved from the served view.
*/
InputConnection mServedInputConnection;
InputConnection ic = view.onCreateInputConnection(tba);
追踪其view
/**
* This is the view that should currently be served by an input method,
* regardless of the state of setting that up.
*/
View mServedView;
最后一步步跟踪赋值,追踪到最后发现回到了view中,如下:
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
而这个this就可能是view的继承对象比如TextView,或继承TextView的EditText,而上面的InputConnection ic = view.onCreateInputConnection(tba);实际就是调用的如下:
@Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
if (onCheckIsTextEditor() && isEnabled()) {
if (mInputMethodState == null) {
mInputMethodState = new InputMethodState();
}
outAttrs.inputType = mInputType;
if (mInputContentType != null) {
outAttrs.imeOptions = mInputContentType.imeOptions;
outAttrs.privateImeOptions = mInputContentType.privateImeOptions;
outAttrs.actionLabel = mInputContentType.imeActionLabel;
outAttrs.actionId = mInputContentType.imeActionId;
outAttrs.extras = mInputContentType.extras;
} else {
outAttrs.imeOptions = EditorInfo.IME_NULL;
}
// if (focusSearch(FOCUS_DOWN) != null) {
// outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
// }
// if (focusSearch(FOCUS_UP) != null) {
// outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
// }
// if ((outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION)
// == EditorInfo.IME_ACTION_UNSPECIFIED) {
// if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0) {
// // An action has not been set, but the enter key will move to
// // the next focus, so set the action to that.
// outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
// } else {
// // An action has not been set, and there is no focus to move
// // to, so let's just supply a "done" action.
// outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE;
// }
// if (!shouldAdvanceFocusOnEnter()) {
// outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
// }
// }
// if (isMultilineInputType(outAttrs.inputType)) {
// // Multi-line text editors should always show an enter key.
// outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
// }
outAttrs.hintText = mHint;
if (mText instanceof Editable) {
InputConnection ic = new EditableInputConnection(this);
outAttrs.initialSelStart = getSelectionStart();
outAttrs.initialSelEnd = getSelectionEnd();
outAttrs.initialCapsMode = ic.getCursorCapsMode(mInputType);
return ic;
}
}
return null;
}
这样你基本明白了吧?这里的InputConnection就是个桥梁,是输入法输入的字符到界面的一个桥梁:InputMethodService与View之间的桥梁