Android输入法显示流程

2020-09-17  本文已影响0人  android_coder

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$InputMethodImpl$showSoftInput
   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;
        }
    }

最终输入法窗口被显示出来

上一篇下一篇

猜你喜欢

热点阅读