进入一个界面,通过一个buttuon点击打开一个输入框,比如无线网络设置蓝牙名称编辑框,这个时候编辑框会默认有焦点,就会默认调用焦点变化的函数TextViewon  FocusChanged方法如下:

    @Override    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {      Log.d(TAG, "TextView class onFocusChanged method");        if (mTemporaryDetach) {            // If we are temporarily in the detach state, then do nothing.            super.onFocusChanged(focused, direction, previouslyFocusedRect);            return;        }

进入super.onFocusChanged方法,如下:

   /**     * Called by the view system when the focus state of this view changes.     * When the focus change event is caused by directional navigation, direction     * and previouslyFocusedRect provide insight into where the focus is coming from.     * When overriding, be sure to call up through to the super class so that     * the standard focus handling will occur.     *     * @param gainFocus True if the View has focus; false otherwise.     * @param direction The direction focus has moved when requestFocus()     *                  is called to give this view focus. Values are     *                  {@link #FOCUS_UP}, {@link #FOCUS_DOWN}, {@link #FOCUS_LEFT},     *                  {@link #FOCUS_RIGHT}, {@link #FOCUS_FORWARD}, or {@link #FOCUS_BACKWARD}.     *                  It may not always apply, in which case use the default.     * @param previouslyFocusedRect The rectangle, in this view's coordinate     *        system, of the previously focused view.  If applicable, this will be     *        passed in as finer grained information about where the focus is coming     *        from (in addition to direction).  Will be null otherwise.     */    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {        if (gainFocus) {            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);        }        InputMethodManager imm = InputMethodManager.peekInstance();        if (!gainFocus) {            if (isPressed()) {                setPressed(false);            }            if (imm != null && mAttachInfo != null                    && mAttachInfo.mHasWindowFocus) {              Log.d(TAG, "View class onFocusChanged method InputMethodManager focusOut");                imm.focusOut(this);            }            onFocusLost();        } else if (imm != null && mAttachInfo != null                && mAttachInfo.mHasWindowFocus) {        Log.d(TAG, "View class onFocusChanged method InputMethodManager focusIn");            imm.focusIn(this);        }        invalidate(true);        ListenerInfo li = mListenerInfo;        if (li != null && li.mOnFocusChangeListener != null) {            li.mOnFocusChangeListener.onFocusChange(this, gainFocus);        }        if (mAttachInfo != null) {            mAttachInfo.mKeyDispatchState.reset(this);        }    }
日志:View class onFocusChanged method InputMethodManager focusIn,从日志来看是调用了imm.focusIn(this);方法,进入该方法,如下:

    /**     * Call this when a view receives focus.     * @hide     */    public void focusIn(View view) {        synchronized (mH) {            focusInLocked(view);        }    }    void focusInLocked(View view) {        if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "focusIn: " + view);                if (mCurRootView != view.getRootView()) {            // This is a request from a window that isn't in the window with            // IME focus, so ignore it.            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Not IME target window, ignoring");            return;        }                mNextServedView = view;        scheduleCheckFocusLocked(view);    }
输出日志:InputMethodManager class focusIn: com.pateo.as.settings.view.AsEditText@418c5850
    void scheduleCheckFocusLocked(View view) {        Handler vh = view.getHandler();        if (vh != null && !vh.hasMessages(ViewRootImpl.CHECK_FOCUS)) {            // This will result in a call to checkFocus() below.            vh.sendMessage(vh.obtainMessage(ViewRootImpl.CHECK_FOCUS));        }    }

我们来看这个CHECK_FOCUS的处理,在相应的类ViewRootImpl中

        case CHECK_FOCUS: {            InputMethodManager imm = InputMethodManager.peekInstance();            if (imm != null) {                imm.checkFocus();            }        } break;

在这里我们看到它继续调用了checkFocus,我们进入该方法

    public void checkFocus() {        if (checkFocusNoStartInput(false)) {          Log.d(TAG, "InputMethodManager class checkFocus method");            startInputInner(null, 0, 0, 0);        }    }

    private boolean checkFocusNoStartInput(boolean forceNewFocus) {        // This is called a lot, so short-circuit before locking.        if (mServedView == mNextServedView && !forceNewFocus) {            return false;        }        InputConnection ic = null;        synchronized (mH) {            if (mServedView == mNextServedView && !forceNewFocus) {                return false;            }            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "checkFocus: view=" + mServedView                    + " next=" + mNextServedView                    + " forceNewFocus=" + forceNewFocus);            if (mNextServedView == null) {                finishInputLocked();                // In this case, we used to have a focused view on the window,                // but no longer do.  We should make sure the input method is                // no longer shown, since it serves no purpose.                closeCurrentInput();                return false;            }            ic = mServedInputConnection;            mServedView = mNextServedView;            mCurrentTextBoxAttribute = null;            mCompletions = null;            mServedConnecting = true;        }        if (ic != null) {            ic.finishComposingText();        }        return true;

我们先在此停顿下,来梳理下上面的方面里面的一些内容,先看日志:

01-01 09:39:14.160 V/PateoInputMethod( 1806): InputMethodManager class checkFocus: view=com.android.internal.policy.impl.PhoneWindow$DecorView@41902530 next=com.pateo.as.settings.view.AsEditText@418c5850 forceNewFocus=false
01-01 09:39:14.160 V/PateoInputMethod( 1806): mServedInputConnection=null

从日志来看InputConnection类型的mServedInputConnection还没有被赋值,重要的是mNextServedView这个不为null是com.pateo.as.settings.view.AsEditText,所以这个时候不用去关闭输入法,看到流程最后返回true,当为true被返回后,执行上面的startInputInner方法,进入该方法

    boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,            int windowFlags) {            final View view;        synchronized (mH) {            view = mServedView;                        // Make sure we have a window token for the served view.            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Starting input: view=" + view);            if (view == null) {                if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "ABORT input: no served view!");                return false;            }        }                // Now we need to get an input connection from the served view.        // This is complicated in a couple ways: we can't be holding our lock        // when calling out to the view, and we need to make sure we call into        // the view on the same thread that is driving its view hierarchy.        Handler vh = view.getHandler();        if (vh == null) {            // If the view doesn't have a handler, something has changed out            // from under us, so just bail.            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "ABORT input: no handler for view!");            return false;        }        if (vh.getLooper() != Looper.myLooper()) {            // The view is running on a different thread than our own, so            // we need to reschedule our work for over there.            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Starting input: reschedule to view thread");            vh.post(new Runnable() {                public void run() {                 Log.d(TAG, "InputMethodManager class startInputInner method");                    startInputInner(null, 0, 0, 0);                }            });            return false;        }                // Okay we are now ready to call into the served view and have it        // do its stuff.        // Life is good: let's hook everything up!        EditorInfo tba = new EditorInfo();        tba.packageName = view.getContext().getPackageName();        tba.fieldId = view.getId();        InputConnection ic = view.onCreateInputConnection(tba);        if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Starting input: tba=" + tba + " ic=" + ic);                synchronized (mH) {            // Now that we are locked again, validate that our state hasn't            // changed.            if (mServedView != view || !mServedConnecting) {                // Something else happened, so abort.                if (DEBUG) Log.v(TAG,"InputMethodManager class " +                          "Starting input: finished by someone else (view="                        + mServedView + " conn=" + mServedConnecting + ")");                return false;            }            // If we already have a text box, then this view is already            // connected so we want to restart it.            if (mCurrentTextBoxAttribute == null) {                controlFlags |= CONTROL_START_INITIAL;            }                        // Hook 'em up and let 'er rip.            mCurrentTextBoxAttribute = tba;            mServedConnecting = false;            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,"InputMethodManager class " +  "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);                }                if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Starting input: Bind result=" + res);                if (res != null) {                    if (res.id != null) {                        mBindSequence = res.sequence;                        mCurMethod = res.method;                    } else if (mCurMethod == null) {                        // This means there is no input method available.                        if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "ABORT input: no input method!");                        return true;                    }                }                if (mCurMethod != null && mCompletions != null) {                    try {                        mCurMethod.displayCompletions(mCompletions);                    } catch (RemoteException e) {                    }                }            } catch (RemoteException e) {                Log.w(TAG,"InputMethodManager class " +  "IME died: " + mCurId, e);            }        }        return true;    }

看流程日志:InputMethodManager class Starting input: view=com.pateo.as.settings.view.AsEditText@418c5850

紧接着看到如下两行日志:

01-01 09:39:14.160 D/PateoInputMethod( 1806): TextView class onCreateInputConnection method start
01-01 09:39:14.160 D/PateoInputMethod( 1806): TextView class onCreateInputConnection return EditableInputConnection

好吧,从日志来看,是执行了如下方法:

InputConnection ic = view.onCreateInputConnection(tba);
此处的view我们已经从上面看出是AsEditText,public class AsEditText extends EditText,它是个EditText,最终回到了EditText的父类TextView,进入到TextView类的onCreateInputConnection方法中

   @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) {        Log.d(TAG, "TextView class onCreateInputConnection method start");            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) {            Log.d(TAG, "TextView class onCreateInputConnection return EditableInputConnection");                InputConnection ic = new EditableInputConnection(this);                outAttrs.initialSelStart = getSelectionStart();                outAttrs.initialSelEnd = getSelectionEnd();                outAttrs.initialCapsMode = ic.getCursorCapsMode(mInputType);                return ic;            }        }        return null;    }

上面代码最重要的是InputConnection ic = new EditableInputConnection(this); return ic;这样就返回了一个InputConnection的子类实现对象EditableInputConnection,而这个EditableInputConnection有个很重要的方法commitText,暂时性记忆这点

跟着我们进行看输出的日志:

01-01 09:39:14.160 V/PateoInputMethod( 1806): InputMethodManager class Starting input: tba=android.view.inputmethod.EditorInfo@41bc3c28 ic=com.android.internal.widget.EditableInputConnection@41bc3f00
01-01 09:39:14.160 V/PateoInputMethod( 1806): InputMethodManager class START INPUT: com.pateo.as.settings.view.AsEditText@418c5850 ic=com.android.internal.widget.EditableInputConnection@41bc3f00 tba=android.view.inputmethod.EditorInfo@41bc3c28 controlFlags=#100

因为windowGainingFocus是传过来的null,所以调用了如下代码:

 res = mService.startInput(mClient,                            servedContext, tba, controlFlags);

这个时候去启动输入法,这里的mService是InputMethodManagerService,进入该方法:

   @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);            }        }    }

    InputBindResult startInputLocked(IInputMethodClient client,            IInputContext inputContext, EditorInfo attribute, int controlFlags) {        // If no method is currently selected, do nothing.        if (mCurMethodId == null) {            return mNoBinding;        }        ClientState cs = mClients.get(client.asBinder());        if (cs == null) {            throw new IllegalArgumentException("unknown client "                    + client.asBinder());        }        try {        //        boolean isInputMethodClientHasFocus = mIWindowManager.inputMethodClientHasFocus(cs.client);//        //        android.util.Log.d(TAG, "InputMethodManagerService class" +  "remove judge focus return null when focos false, isInputMethodClientHasFocus=" + isInputMethodClientHasFocus);                     if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {                // Check with the window manager to make sure this client actually                // has a window with focus.  If not, reject.  This is thread safe                // because if the focus changes some time before or after, the                // next client receiving focus that has any interest in input will                // be calling through here after that change happens.                Slog.w(TAG, "InputMethodManagerService class" +  "Starting input on non-focused client " + cs.client                        + " (uid=" + cs.uid + " pid=" + cs.pid + ")");                return null;            }        } catch (RemoteException e) {        }        return startInputUncheckedLocked(cs, inputContext, attribute, controlFlags);    }

    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.                return attachNewInputLocked(                        (controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0);            }

看到最后的return attachNewInputLocked方法

    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);    }

上面的代码跟踪,做后进入到了MSG_START_INPUT这个方法中,我们进行跟进

          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) {                }                return true;
最终进入到了InputMethodService类的startInput方法中

        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");        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);            }        }    }

看下相应的日志:

01-01 09:39:14.160 V/PateoInputMethod( 1461): InputMethodService classstartInput(): editor=android.view.inputmethod.EditorInfo@418f80b0
01-01 09:39:14.160 V/PateoInputMethod( 1461): InputMethodService classCALL: onFinishInput

进入了doFinishInput方法

   void doFinishInput() {        if (mInputViewStarted) {            if (DEBUG) Log.v(TAG,"InputMethodService class" +  "CALL: onFinishInputView");            onFinishInputView(true);        } else if (mCandidatesViewStarted) {            if (DEBUG) Log.v(TAG,"InputMethodService class" +  "CALL: onFinishCandidatesView");            onFinishCandidatesView(true);        }        mInputViewStarted = false;        mCandidatesViewStarted = false;        if (mInputStarted) {            if (DEBUG) Log.v(TAG,"InputMethodService class" +  "CALL: onFinishInput");            onFinishInput();        }        mInputStarted = false;        mStartedInputConnection = null;        mCurCompletions = null;    }

到这里结束了,需要进一步回头看一些参数,下面再分析









更多相关文章

  1. Android(安卓)DataBinding使用详解(一)
  2. 使用 SQLiteDatabase 操作 SQLite 数据库
  3. Android菜鸟笔记-调用相机拍照后返回照片过小的问题
  4. Android(安卓)studio 中与本地 html 页面交互
  5. ContentProvider中gettype() 和MIME类型的理解
  6. Android(安卓)开发者必知必会的权限管理知识
  7. 真机上使用Hierarchy Viewer
  8. Android(安卓)studio 命令gradlew assembleRelease打包时,出现 Un
  9. android类作用整理

随机推荐

  1. Android之SystemUI加载流程和NavigationB
  2. Android视图的截图
  3. android:shape的使用
  4. Android的onCreateOptionsMenu()创建菜单
  5. Android(安卓)开发技术周报 Issue#275
  6. Android牟利之道(一)--界面嵌入有米广告
  7. Android(安卓)Fragment 生命周期及回调方
  8. android 动画
  9. 申请 android google map API key
  10. 关于Google Android,第一印象