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



当我们从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的时候会用到上面的变量,输入法的变量而且比较多,需要慢慢梳理









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值