android开发浅谈之InputMethodManagerService

Posted hfreeman2008

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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) 
    Log.v(TAG, "InputMethodManager "  ,new Throwable());

其打印信息:

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));
     Log.v(TAG, "startInputInner mService.startInputOrWindowGainedFocus"  ,new Throwable());
     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 getcaller:"  ,new Throwable());
    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以上是关于android开发浅谈之InputMethodManagerService的主要内容,如果未能解决你的问题,请参考以下文章

android开发浅谈之InputMethodManagerService

android开发浅谈之InputMethodManagerService

android开发浅谈之PackageManagerService(pkms)

android开发浅谈之PackageManagerService(pkms)

android开发浅谈之InputMethodManagerService

android开发浅谈之PackageManagerService部分一(pkms)