android开发浅谈之InputMethodManagerService

来到新公司,负责了输入法的问题处理和一些输入法相关的功能开发,所以对输入法有了一点点了解,所以写了这篇文章来从系统的角度浅谈一下输入法。

输入法管理服务的整体框架

输入法的整件框架:
在这里插入图片描述
输入法管理服务InputMethodManagerService主要包括三个模块:

  • 第一个是app应用进程:

此部分可以使用InputMethodManager类来发起显示输入法或隐藏输入法的请求,也可以配置输入法的一些属性。

  • 第二个是输入法应用进程:

这个就是我们通常意义上说的输入法应用,其主是InputMethodService的一个实现类,如qq输入法,搜狗输入法。

  • 第三个是系统SystemServer进程的InputMethodManagerService类:

这个才是输入法的管理核心类。

输入法应用–InputMethodService

我们先讨论输入法应用部分:
以android内置的输入法LatinIME为例,

先看LatinIME输入的AndroidManifest.xml对其定义:

<service android:name="LatinIME"
        android:label="@string/english_ime_name"
        android:permission="android.permission.BIND_INPUT_METHOD">
    <intent-filter>
        <action android:name="android.view.InputMethod" />
    </intent-filter>
    <meta-data android:name="android.view.im" android:resource="@xml/method" />
</service>

从这可以看出,LatinIME是一个service。

再看LatinIME类:

public class LatinIME extends InputMethodService implements KeyboardActionListener,
        SuggestionStripView.Listener, SuggestionStripViewAccessor,
        DictionaryFacilitator.DictionaryInitializationListener,
        PermissionsManager.PermissionsResultCallback {

可以看出,输入法LatinIME是InputMethodService的实现类。

那InputMethodService是什么呢?

请看下面InputMethodService的类图:
在这里插入图片描述

InputMethodService的类图清楚的表示其是一个带Dialog的Service,所以,输入法应用可以简单的理解为一个带Dialog的Service。

InputMethodService类中的mInputView 对应类LatinIME.mInputView,而LatinIME.mInputView对应KeyboardSwitcher.onCreateInputView方法中的mCurrentInputView :

public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
	......
    mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
            R.layout.input_view, null);

下面我们讲解app应用如何显示输入法界面,而InputMethodManagerService类是如何响应app的调用显示输入法。

app应用调用显示输入法和隐藏输入法

以我们在app应用中,点击一个输入框,显示输入法为例:

1.初始化InputMethodManager:

在InputMethodManager.InputMethodManager添加堆栈信息:

private InputMethodManager(IInputMethodManager service, int displayId, Looper looper) {

其打印信息:

android.view.inputmethod.InputMethodManager.<init>(InputMethodManager.java:955)
android.view.inputmethod.InputMethodManager.createRealInstance(InputMethodManager.java:904)
android.view.inputmethod.InputMethodManager.createInstance(InputMethodManager.java:892)
android.view.inputmethod.InputMethodManager.forContextInternal(InputMethodManager.java:988)
android.view.inputmethod.InputMethodManager.forContext(InputMethodManager.java:977)
android.app.SystemServiceRegistry$27.getService(SystemServiceRegistry.java:457)
android.app.SystemServiceRegistry$27.getService(SystemServiceRegistry.java:454)
android.app.SystemServiceRegistry.getSystemService(SystemServiceRegistry.java:1366)
android.app.ContextImpl.getSystemService(ContextImpl.java:1809)
android.content.ContextWrapper.getSystemService(ContextWrapper.java:752)

2.调用InputMethodManager.startInputInner接口

在InputMethodManager.startInputInner中添加堆栈信息:

boolean startInputInner(@StartInputReason int startInputReason,
        @Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags,
        @SoftInputModeFlags int softInputMode, int windowFlags) {
        ......
     try {
     if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
             + ic + " tba=" + tba + " startInputFlags="
             + InputMethodDebug.startInputFlagsToString(startInputFlags));
     final InputBindResult res = mService.startInputOrWindowGainedFocus(
             startInputReason, mClient, windowGainingFocus, startInputFlags,
             softInputMode, windowFlags, tba, servedContext, missingMethodFlags,
             view.getContext().getApplicationInfo().targetSdkVersion);
             ......

其打印信息:

android.view.inputmethod.InputMethodManager.startInputInner(InputMethodManager.java:1772)
android.view.inputmethod.InputMethodManager.checkFocus(InputMethodManager.java:1920)
android.view.inputmethod.InputMethodManager.viewClicked(InputMethodManager.java:2204)
android.widget.TextView.viewClicked(TextView.java:12914)
android.widget.TextView.onTouchEvent(TextView.java:10915)
android.view.View.dispatchTouchEvent(View.java:13980)
android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3083)
android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2778)
android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3083)
android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2778)
android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3083)
android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2778)
android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3083)
android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2778)
android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3083)
android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2778)
android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3083)
android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2778)
com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:481)
com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1854)
android.app.Activity.dispatchTouchEvent(Activity.java:4030)
androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:439)
android.view.View.dispatchPointerEvent(View.java:14239)
android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5767)
android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5564)
android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5067)
android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5120)
android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5086)
android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5226)
android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5094)
android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5283)
android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5067)
android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5120)
android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5086)
android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5094)
android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5067)
android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7796)
android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7765)
android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7726)
android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7921)
android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:189)
android.os.MessageQueue.nativePollOnce(Native Method)
android.os.MessageQueue.next(MessageQueue.java:336)
android.os.Looper.loop(Looper.java:181)
android.app.ActivityThread.main(ActivityThread.java:7574)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)

我们简单的看一下几个核心的接口:

TextView.onTouchEvent

public boolean onTouchEvent(MotionEvent event) {
......
   if (touchIsFinished && (isTextEditable() || textIsSelectable)) {
       // Show the IME, except when selecting in read-only text.
       final InputMethodManager imm = getInputMethodManager();
       viewClicked(imm);//确认view click
       if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
           imm.showSoftInput(this, 0);//显示输入法
       }
......

TextView.viewClicked

protected void viewClicked(InputMethodManager imm) {
    if (imm != null) {
        imm.viewClicked(this);//调用imm.viewClicked
    }
}

InputMethodManager.viewClicked

public void viewClicked(View view) {
.....
    final boolean focusChanged = mServedView != mNextServedView;
    checkFocus();//调用checkFocus

InputMethodManager.checkFocus

public void checkFocus() {
    if (checkFocusNoStartInput(false)) {
        startInputInner(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);//调用startInputInner
    }
}

InputMethodManager.startInputInner

boolean startInputInner(@StartInputReason int startInputReason,
        @Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags,
        @SoftInputModeFlags int softInputMode, int windowFlags) {
   ......
        try {
         if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
                 + ic + " tba=" + tba + " startInputFlags="
                 + InputMethodDebug.startInputFlagsToString(startInputFlags));
         final InputBindResult res = mService.startInputOrWindowGainedFocus(
                 startInputReason, mClient, windowGainingFocus, startInputFlags,
                 softInputMode, windowFlags, tba, servedContext, missingMethodFlags,
                 view.getContext().getApplicationInfo().targetSdkVersion);//调用mService.startInputOrWindowGainedFocus
   ......

我们可以从上面的日志信息明确看到我们点击输入框,系统如何从:
TextView.onTouchEvent----InputMethodManager.viewClicked----InputMethodManager.startInputInner----InputMethodManagerService.startInputOrWindowGainedFocus

下面就是其类图:
在这里插入图片描述

3.InputMethodManagerService类发送消息MSG_START_INPUT

在InputMethodManagerService.handleMessage中添加堆栈信息:

public boolean handleMessage(Message msg) {
    Slog.v(TAG, "handleMessage msg.what:"  + msg.what);

在接上面InputMethodManagerService.startInputOrWindowGainedFocus接口调用后,打印日志信息:

com.android.server.inputmethod.InputMethodManagerService.handleMessage(InputMethodManagerService.java:3810)
com.android.server.inputmethod.InputMethodManagerService.executeOrSendMessage(InputMethodManagerService.java:2013)
com.android.server.inputmethod.InputMethodManagerService.attachNewInputLocked(InputMethodManagerService.java:2080)
com.android.server.inputmethod.InputMethodManagerService.startInputUncheckedLocked(InputMethodManagerService.java:2208)
com.android.server.inputmethod.InputMethodManagerService.startInputOrWindowGainedFocusInternalLocked(InputMethodManagerService.java:3171)
com.android.server.inputmethod.InputMethodManagerService.startInputOrWindowGainedFocus(InputMethodManagerService.java:3076)
com.android.internal.view.IInputMethodManager$Stub.onTransact(IInputMethodManager.java:331)
com.android.server.inputmethod.InputMethodManagerService.onTransact(InputMethodManagerService.java:1636)
android.os.Binder.execTransactInternal(Binder.java:1021)
android.os.Binder.execTransact(Binder.java:994)
handleMessage msg.what:2000

通过上面信息,我们知道其调用过程:

先查看InputMethodManagerService.startInputOrWindowGainedFocus:

public InputBindResult startInputOrWindowGainedFocus(
        @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
        @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
        int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
        @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
    ....
     final InputBindResult result;
     synchronized (mMethodMap) {
     final long ident = Binder.clearCallingIdentity();
     try {
         result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client,
                 windowToken, startInputFlags, softInputMode, windowFlags, attribute,
                 inputContext, missingMethods, unverifiedTargetSdkVersion, userId);//调用startInputOrWindowGainedFocusInternalLocked

InputMethodManagerService.startInputOrWindowGainedFocusInternalLocked

private InputBindResult startInputOrWindowGainedFocusInternalLocked(
        @StartInputReason int startInputReason, IInputMethodClient client,
        @NonNull IBinder windowToken, @StartInputFlags int startInputFlags,
        @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
        IInputContext inputContext, @MissingMethodFlags int missingMethods,
        int unverifiedTargetSdkVersion, @UserIdInt int userId) {
    ......
    if (mCurFocusedWindow == windowToken) {
    if (DEBUG) {
        Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
                + " attribute=" + attribute + ", token = " + windowToken);
    }
    if (attribute != null) {
        return startInputUncheckedLocked(cs, inputContext, missingMethods,
                attribute, startInputFlags, startInputReason);//调用startInputUncheckedLocked

InputMethodManagerService.startInputUncheckedLocked

InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
        @MissingMethodFlags int missingMethods, @NonNull EditorInfo attribute,
        @StartInputFlags int startInputFlags, @StartInputReason int startInputReason) {
    ......
    if (mCurId != null && mCurId.equals(mCurMethodId)
        && displayIdToShowIme == mCurTokenDisplayId) {
    if (cs.curSession != null) {
        // Fast case: if we are already connected to the input method,
        // then just return it.
        return attachNewInputLocked(startInputReason,
                (startInputFlags & StartInputFlags.INITIAL_CONNECTION) != 0);//调用attachNewInputLocked

InputMethodManagerService.attachNewInputLocked

InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
......
    final SessionState session = mCurClient.curSession;
    executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
            MSG_START_INPUT, mCurInputContextMissingMethods, initial ? 0 : 1 /* restarting */,
            startInputToken, session, mCurInputContext, mCurAttribute));//发送MSG_START_INPUT消息

发送MSG_START_INPUT消息,对应接受到的消息为2000

handleMessage msg.what:2000

MSG_START_INPUT定义为:

static final int MSG_START_INPUT = 2000;

接受到的消息为2000后,对应调用:

public boolean handleMessage(Message msg) {
    ......
    switch (msg.what) {
    ......
         case MSG_START_INPUT: {
         final int missingMethods = msg.arg1;
         final boolean restarting = msg.arg2 != 0;
         args = (SomeArgs) msg.obj;
         final IBinder startInputToken = (IBinder) args.arg1;
         final SessionState session = (SessionState) args.arg2;
         final IInputContext inputContext = (IInputContext) args.arg3;
         final EditorInfo editorInfo = (EditorInfo) args.arg4;
         try {
             setEnabledSessionInMainThread(session);
             session.method.startInput(startInputToken, inputContext, missingMethods,
                     editorInfo, restarting, session.client.shouldPreRenderIme);//调用method.startInput方法
         } catch (RemoteException e) {
         }
         args.recycle();
         return true;
     }

对应调用session.method.startInput为:
InputMethodService.InputMethodImpl.startInput方法:

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

4.InputMethodManager.showSoftInput

在InputMethodManager.showSoftInput添加日志信息:

public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
......
        try {
            return mService.showSoftInput(mClient, flags, resultReceiver);

打印日志信息如下:

android.view.inputmethod.InputMethodManager.showSoftInput(InputMethodManager.java:1444)
android.view.inputmethod.InputMethodManager.showSoftInput(InputMethodManager.java:1368)
android.widget.TextView.onTouchEvent(TextView.java:10917)

查看TextView.onTouchEvent:

public boolean onTouchEvent(MotionEvent event) {
......
      viewClicked(imm);
      if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
          imm.showSoftInput(this, 0);
      }

InputMethodManager.showSoftInput

public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
......
    try {
        return mService.showSoftInput(mClient, flags, resultReceiver);//在此调用mService.showSoftInput

5.InputMethodManagerService类发送消息MSG_SHOW_SOFT_INPUT

输出InputMethodManagerService调用日志信息:

com.android.server.inputmethod.InputMethodManagerService.handleMessage(InputMethodManagerService.java:3810)
com.android.server.inputmethod.InputMethodManagerService.executeOrSendMessage(InputMethodManagerService.java:2013)
com.android.server.inputmethod.InputMethodManagerService.showCurrentInputLocked(InputMethodManagerService.java:2890)
com.android.server.inputmethod.InputMethodManagerService.showSoftInput(InputMethodManagerService.java:2862)

handler处理的消息为:1020

handleMessage msg.what:1020

其1020消息的定义为:

static final int MSG_SHOW_SOFT_INPUT = 1020;

我们查看一下上面的核心关键代码:
InputMethodManagerService.showSoftInput

public boolean showSoftInput(IInputMethodClient client, int flags,
        ResultReceiver resultReceiver) {
    ....
    return showCurrentInputLocked(flags, resultReceiver);

InputMethodManagerService.showCurrentInputLocked

boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
......
   if (mCurMethod != null) {
      if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken);
      executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
              MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod,
              resultReceiver));//发送MSG_SHOW_SOFT_INPUT消息
public boolean handleMessage(Message msg) {
    ......
    switch (msg.what) {
    ......
     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;

调用InputMethodService.InputMethodImpl.showSoftInput:

public void showSoftInput(int flags, ResultReceiver resultReceiver) {
.......
showWindow(true);

InputMethodService.showWindow

public void showWindow(boolean showInput) {
......
mWindow.show();

整个点击输入框显示输入法的流程图为:
在这里插入图片描述

在搜狗输入法上隐藏输入法

在InputMethodPrivilegedOperations.hideMySoftInput中添加堆栈信息:

public void hideMySoftInput(int flags) {

app应用层打印出日志信息:

com.android.internal.inputmethod.InputMethodPrivilegedOperations.hideMySoftInput(InputMethodPrivilegedOperations.java:249)
android.inputmethodservice.InputMethodService.requestHideSelf(InputMethodService.java:2279)
com.sohu.inputmethod.sogou.SogouIME.requestHideSelf(SogouSource:586)

我们查看一下关键代码信息:
InputMethodService.requestHideSelf

public void requestHideSelf(int flags) {
    mPrivOps.hideMySoftInput(flags);
}

InputMethodPrivilegedOperations.hideMySoftInput

public void hideMySoftInput(int flags) {
    final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
    if (ops == null) {
        return;
    }
    try {
        ops.hideMySoftInput(flags);//调用ops.hideMySoftInput
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

system server 对应打印出日志信息:

com.android.server.inputmethod.InputMethodManagerService.handleMessage(InputMethodManagerService.java:3810)
com.android.server.inputmethod.InputMethodManagerService.executeOrSendMessage(InputMethodManagerService.java:2013)
com.android.server.inputmethod.InputMethodManagerService.hideCurrentInputLocked(InputMethodManagerService.java:3001)
com.android.server.inputmethod.InputMethodManagerService.hideMySoftInput(InputMethodManagerService.java:3762)
com.android.server.inputmethod.InputMethodManagerService.access$3100(InputMethodManagerService.java:192)
com.android.server.inputmethod.InputMethodManagerService$InputMethodPrivilegedOperationsImpl.hideMySoftInput(InputMethodManagerService.java:5365)

我们查看关键代码信息:
InputMethodManagerService$InputMethodPrivilegedOperationsImpl.hideMySoftInput

public void hideMySoftInput(int flags) {
    mImms.hideMySoftInput(mToken, flags);
}

InputMethodManagerService.hideMySoftInput

private void hideMySoftInput(@NonNull IBinder token, int flags) {
    synchronized (mMethodMap) {
        if (!calledWithValidTokenLocked(token)) {
            return;
        }
        long ident = Binder.clearCallingIdentity();
        try {
            hideCurrentInputLocked(flags, null);

InputMethodManagerService.hideCurrentInputLocked

boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
    ......
    executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                    MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver));

InputMethodManagerService.handleMessage

public boolean handleMessage(Message msg) {
......
        case MSG_HIDE_SOFT_INPUT:
            args = (SomeArgs)msg.obj;
            try {
                if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
                        + args.arg2 + ")");
                ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2);
            } catch (RemoteException e) {
            }
            args.recycle();
            return true;

InputMethodService.hideSoftInput

public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
......
      mShowInputFlags = 0;
      mShowInputRequested = false;
      doHideWindow();

InputMethodService.doHideWindow

private void doHideWindow() {
    setImeWindowStatus(0, mBackDisposition);
    hideWindow();
}

InputMethodService.hideWindow

public void hideWindow() {
    if (DEBUG) Log.v(TAG, "CALL: hideWindow");
    mIsPreRendered = false;
    mWindowVisible = false;
    finishViews(false /* finishingInput */);
    if (mDecorViewVisible) {
        mWindow.hide();
        mDecorViewVisible = false;
        onWindowHidden();
        mDecorViewWasVisible = false;
    }
    updateFullscreenMode();
}

我们可以得出完整的隐藏输入法的流程图:
在这里插入图片描述

输入法的日志开关

事实上,我们平时分析输入法的问题时,需要打开输入法的日志开关,才能更好的分析定位问题

frameworks\base\services\core\java\com\android\server\inputmethod\InputMethodManagerService.java
static final boolean DEBUG = false;

frameworks\base\services\core\java\com\android\server\wm\WindowManagerDebugConfig.java
static final boolean DEBUG_INPUT_METHOD = false;

frameworks\base\core\java\android\view\inputmethod\InputMethodManager.java
static final boolean DEBUG = false;

frameworks\base\core\java\android\inputmethodservice\InputMethodService.java
static final boolean DEBUG = false;

frameworks\base\services\core\java\com\android\server\inputmethod\InputMethodUtils.java
public static final boolean DEBUG = false;

frameworks\base\services\core\java\com\android\server\inputmethod\InputMethodSubtypeSwitchingController.java
private static final boolean DEBUG = false;

frameworks\base\core\java\android\inputmethodservice\SoftInputWindow.java
    private static final boolean DEBUG = true;//false;

输入法的dump信息

adb shell dumpsys input_method

参考资料

1.Android输入法框架系统(上)
https://blog.csdn.net/ITleaks/article/details/27398453
2.Android输入法框架系统(下)
https://blog.csdn.net/ITleaks/article/details/27480403
3.InputMethodManager
https://developer.android.google.cn/reference/android/view/inputmethod/InputMethodManager?hl=en

  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hfreeman2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值