您的位置:首页 > 移动开发 > Android开发

android 输入法WINDOW_FOCUS_CHANGED|MSG_CREATE_SESSION|MSG_BIND_INPUT|MSG_START_INPUT|MSG_BIND_METHOD消息

2014-05-05 18:20 387 查看
当我们从home界面进入到设置界面应用,这个时候设置应用下没有任何输入框,但是这个时候foucs进行了change,而输入法也将进行一次尝试性判断是否需要显示输入法

ViewRootImpl.java 的 case WINDOW_FOCUS_CHANGED: {处理

InputMethodManager imm = InputMethodManager.peekInstance();
Log.v("PateoInputMethod","ViewRootImpl class " + " ,WINDOW_FOCUS_CHANGED msg,");
if (mView != null) {
if (hasWindowFocus && imm != null && mLastWasImTarget) {
Log.v("PateoInputMethod","ViewRootImpl class " + " ,WINDOW_FOCUS_CHANGED msg, imm.startGettingWindowFocus mView=" + mView);
imm.startGettingWindowFocus(mView);
}
mAttachInfo.mKeyDispatchState.reset();
mView.dispatchWindowFocusChanged(hasWindowFocus);
}

// Note: must be done after the focus change callbacks,
// so all of the view state is set up correctly.
if (hasWindowFocus) {
if (imm != null && mLastWasImTarget) {
Log.v("PateoInputMethod","ViewRootImpl class " + " ,WINDOW_FOCUS_CHANGED msg,imm.onWindowFocus");
imm.onWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}


输出日志:

01-01 09:41:43.840 V/PateoInputMethod( 1757): ViewRootImpl class  ,WINDOW_FOCUS_CHANGED msg,
01-01 09:41:43.840 V/PateoInputMethod( 1757): ViewRootImpl class  ,WINDOW_FOCUS_CHANGED msg, imm.startGettingWindowFocus mView=com.android.internal.policy.impl.PhoneWindow$DecorView@418f5518
01-01 09:41:43.860 V/PateoInputMethod( 1757): ViewRootImpl class  ,WINDOW_FOCUS_CHANGED msg,imm.onWindowFocus


从上面来看,我们进入imm.onWindowFocus的方法进行分析

/**
* Called by ViewAncestor when its window gets input focus.
* @hide
*/
public void onWindowFocus(View rootView, View focusedView, int softInputMode,
boolean first, int windowFlags) {
boolean forceNewFocus = false;
synchronized (mH) {
if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "onWindowFocus: " + focusedView
+ " softInputMode=" + softInputMode
+ " first=" + first + " flags=#"
+ Integer.toHexString(windowFlags));
if (mHasBeenInactive) {
if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Has been inactive!  Starting fresh");
mHasBeenInactive = false;
forceNewFocus = true;
}
focusInLocked(focusedView != null ? focusedView : rootView);
}

int controlFlags = 0;
if (focusedView != null) {
controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
if (focusedView.onCheckIsTextEditor()) {
controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
}
}
if (first) {
controlFlags |= CONTROL_WINDOW_FIRST;
}

if (checkFocusNoStartInput(forceNewFocus)) {
// We need to restart input on the current focus view.  This
// should be done in conjunction with telling the system service
// about the window gaining focus, to help make the transition
// smooth.
Log.d(TAG, "InputMethodManager class onWindowFocus method");
if (startInputInner(rootView.getWindowToken(),
controlFlags, softInputMode, windowFlags)) {
return;
}
}

// For some reason we didn't do a startInput + windowFocusGain, so
// we'll just do a window focus gain and call it a day.
synchronized (mH) {
try {
mService.windowGainedFocus(mClient, rootView.getWindowToken(),
controlFlags, softInputMode, windowFlags, null, null);
} catch (RemoteException e) {
}
}
}


相应的日志输入Log.d(TAG, "InputMethodManager class onWindowFocus method");  发现进入了startInputInner方法,在此方法中,根据相应的输出日志我们发现走入了如下代码行:

if (windowGainingFocus != null) {
if (DEBUG) Log.v(TAG,"InputMethodManager class " + " IInputMethodManager.windowGainedFocus");
res = mService.windowGainedFocus(mClient, windowGainingFocus,
controlFlags, softInputMode, windowFlags,
tba, servedContext);
}
进入该InputMethodManagerService的windowGainedFocus方法

switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
if (!isTextEditor || !doAutoShow) {
if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) {
// There is no focus view, and this window will
// be behind any soft input window, so hide the
// soft input window if it is shown.
if (DEBUG) Slog.v(TAG, "InputMethodManagerService class" +  "Unspecified window will hide input");
hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null);
}
}


输出日志:Unspecified window will hide input,在上面的switch代码行下面还有如下必覆盖代码:

if (!didStart && attribute != null) {
res = startInputUncheckedLocked(cs, inputContext, attribute,
controlFlags);
}


进入该startInputUncheckedLocked方法

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;
mCurAttribute = attribute;

// Check if the input method is changing.
if (mCurId != null && mCurId.equals(mCurMethodId)) {
if (cs.curSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
Slog.w(TAG, "InputMethodManagerService class" + "startInputUncheckedLocked method ,attachNewInputLocked");
return attachNewInputLocked(
(controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0);
}
if (mHaveConnection) {
if (mCurMethod != null) {
if (!cs.sessionRequested) {
cs.sessionRequested = true;
if (DEBUG) Slog.v(TAG, "InputMethodManagerService class" +  "Creating new session for client " + cs);
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_CREATE_SESSION, mCurMethod,
new MethodCallback(mCurMethod, this)));
}
// Return to client, and we will get back with it when
// we have had a session made for it.
return new InputBindResult(null, mCurId, mCurSeq);
} else if (SystemClock.uptimeMillis()
< (mLastBindTime+TIME_TO_RECONNECT)) {
// In this case we have connected to the service, but
// don't yet have its interface.  If it hasn't been too
// long since we did the connection, we'll return to
// the client and wait to get the service interface so
// we can report back.  If it has been too long, we want
// to fall through so we can try a disconnect/reconnect
// to see if we can get back in touch with the service.
return new InputBindResult(null, mCurId, mCurSeq);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
}
}
}

return startInputInnerLocked();
}


我们先看上面方法中的如下一行代码

unbindCurrentClientLocked();


进入该方法

void unbindCurrentClientLocked() {
if (mCurClient != null) {
if (DEBUG) Slog.v(TAG, "InputMethodManagerService class" +  "unbindCurrentInputLocked: client = "
+ mCurClient.client.asBinder());
if (mBoundToMethod) {
mBoundToMethod = false;
if (mCurMethod != null) {
executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
MSG_UNBIND_INPUT, mCurMethod));
}
}

try {
mCurSeq = ((IInputMethodClient)mCurClient.client).getBindSequence();
} catch (RemoteException e) {

}

executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
mCurClient.sessionRequested = false;

// Call setActive(false) on the old client
try {
mCurClient.client.setActive(false);
} catch (RemoteException e) {
Slog.w(TAG, "InputMethodManagerService class" +  "Got RemoteException sending setActive(false) notification to pid "
+ mCurClient.pid + " uid " + mCurClient.uid);
}
mCurClient = null;

hideInputMethodMenuLocked();
}
}


因为mCurClient == null所以该放入内代码未覆盖执行,但是这里需要注意下,如果mCurClient != null 则会MSG_UNBIND_INPUT、MSG_UNBIND_METHOD即其它

这里我们回到startInputUncheckedLocked方法,紧接着我们走入了如下代码:

if (mHaveConnection) {
if (mCurMethod != null) {
if (!cs.sessionRequested) {
cs.sessionRequested = true;
if (DEBUG) Slog.v(TAG, "InputMethodManagerService class" +  "Creating new session for client " + cs);
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_CREATE_SESSION, mCurMethod,
new MethodCallback(mCurMethod, this)));
}
// Return to client, and we will get back with it when
// we have had a session made for it.
return new InputBindResult(null, mCurId, mCurSeq);
}


从这里来看我们开始MSG_CREATE_SESSION消息的处理

case MSG_CREATE_SESSION:
Slog.d(TAG,"InputMethodManagerService class MSG_CREATE_SESSION msg ");
args = (HandlerCaller.SomeArgs)msg.obj;
try {
((IInputMethod)args.arg1).createSession(
(IInputMethodCallback)args.arg2);
} catch (RemoteException e) {
}
return true;


此进入了当前类的回调sessionCreated

private static class MethodCallback extends IInputMethodCallback.Stub {
private final IInputMethod mMethod;
private final InputMethodManagerService mParentIMMS;

MethodCallback(final IInputMethod method, final InputMethodManagerService imms) {
mMethod = method;
mParentIMMS = imms;
}

@Override
public void finishedEvent(int seq, boolean handled) throws RemoteException {
Slog.i(TAG, "InputMethodManagerService class" + " ,MethodCallback finishedEvent method");
}

@Override
public void sessionCreated(IInputMethodSession session) throws RemoteException {
Slog.i(TAG, "InputMethodManagerService class" + " ,MethodCallback sessionCreated method");
mParentIMMS.onSessionCreated(mMethod, session);
}
}


void onSessionCreated(IInputMethod method, IInputMethodSession session) {
synchronized (mMethodMap) {
if (mCurMethod != null && method != null
&& mCurMethod.asBinder() == method.asBinder()) {
if (mCurClient != null) {
mCurClient.curSession = new SessionState(mCurClient,
method, session);
mCurClient.sessionRequested = false;
Slog.w(TAG, "InputMethodManagerService class" + "onSessionCreated method ,attachNewInputLocked");
InputBindResult res = attachNewInputLocked(true);
if (res.method != null) {
Slog.w(TAG, "InputMethodManagerService class" + "onSessionCreated method ,attachNewInputLocked MSG_BIND_METHOD");
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
MSG_BIND_METHOD, mCurClient.client, res));
}
}
}
}
}


相应的输出日志:
onSessionCreated method ,attachNewInputLocked

attachNewInputLocked method coming...,mBoundToMethod=false ,initial=true

onSessionCreated method ,attachNewInputLocked MSG_BIND_METHOD

InputBindResult attachNewInputLocked(boolean initial) {
if (DEBUG) Slog.v(TAG, "InputMethodManagerService class" +  "attachNewInputLocked method coming...,mBoundToMethod=" + mBoundToMethod + " ,initial=" + 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);
}
if (DEBUG) Slog.v(TAG, "InputMethodManagerService class" +  "attachNewInputLocked method coming...");
return new InputBindResult(session.session, mCurId, mCurSeq);
}


从上面来看,执行了MSG_BIND_INPUT、MSG_START_INPUT、MSG_BIND_METHOD消息,上面即初始化的过程,而这个过程中虽然MSG_START_INPUT了,但是这个最终调用InputMethodService

public void startInput(InputConnection ic, EditorInfo attribute) {
if (DEBUG) Log.v(TAG,"InputMethodService class" +  "startInput(): editor=" + attribute);
doStartInput(ic, attribute, false);
}


进入该方法

void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
if (!restarting) {
doFinishInput();
}
mInputStarted = true;
mStartedInputConnection = ic;
mInputEditorInfo = attribute;
initialize();
if (DEBUG) Log.v(TAG,"InputMethodService class" +  "CALL: onStartInput , mWindowVisible=" + mWindowVisible);
onStartInput(attribute, restarting);
if (mWindowVisible) {
if (mShowInputRequested) {
if (DEBUG) Log.v(TAG,"InputMethodService class" +  "CALL: onStartInputView");
mInputViewStarted = true;
onStartInputView(mInputEditorInfo, restarting);
startExtractingText(true);
} else if (mCandidatesVisibility == View.VISIBLE) {
if (DEBUG) Log.v(TAG,"InputMethodService class" +  "CALL: onStartCandidatesView");
mCandidatesViewStarted = true;
onStartCandidatesView(mInputEditorInfo, restarting);
}
}
}


看输入日志InputMethodService classCALL: onStartInput , mWindowVisible=false,因为mWindowVisible=false所以未显示输入法界面

上面的初始化过程很重要,很多对象进行了初始化在此过程中,后续MSG_SHOW_SOFT_INPUT的时候会用到上面的变量,输入法的变量而且比较多,需要慢慢梳理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐