android开发浅谈之inputmethodmanagerservice(代码片段)

hfreeman2008 hfreeman2008     2022-11-04     487

关键词:

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

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

输入法的整件框架:

输入法管理服务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.java:994)
查看详情  

android开发浅谈之写在前面的话

...相关的职业选择了2011年8月----2013年8月深圳康佳通信科技androidapp开发刚入行,有点怕怕。带我的领导是一位北大的美女,智商碾压绝大部分人&# 查看详情

android开发浅谈之inputmethodmanagerservice(代码片段)

...应用–InputMethodService我们先讨论输入法应用部分:以android内置的输入法LatinIME为例,先看LatinIME输入的AndroidManifest.xml对其定义:<serviceandroid:name="LatinIME"android:label="@st 查看详情

android开发浅谈之写在前面的话

...相关的职业选择了2011年8月----2013年8月深圳康佳通信科技androidapp开发刚入行,有点怕怕。带我的领导是一位北大的美女,智商碾压绝大部分人,公司要求比较高,比较严,技术开发特别规范。本以后这会是我... 查看详情

浅谈之merge-sort-join

初接触执行计划,做练习时执行sql(如图一)图一:查看其执行计划(如图二)图二:看到上面这个执行计划用到mergesortjoin(排序合并联合查询),刚开始没有理解为什么这条执行计划里还有sortjoin,emp表里deptno字段不是已经有... 查看详情

qt浅谈之二:钟表(时分秒针),qsplitter实现自由伸缩滑动窗口good

http://blog.csdn.net/taiyang1987912/article/details/30272105http://blog.csdn.net/taiyang1987912/article/details/50717179http://blog.csdn.net/taiyang1987912/article/category/2314763 查看详情

qt浅谈之二十一log调试日志

一、简单介绍     近期因调试code时,想了解程序的流程,但苦于没有一个简易的日志记录,不停使用qDebug打印输出,而终于提交代码时得去多次删除信息打印,有时还会出现新改动的代码分不清是哪些部分。... 查看详情

设计模式浅谈之----设计模式简介

  1.何为设计模式设计模式是一个通过定义、使用和测试去解决特定问题的方法,并且由于设计模式是在面向对象之后为人所知的,基本思想与面向对象不可分割。在软件工程中,设计模式是一般只在给定条件下会重复性... 查看详情

qt浅谈之四十二钟表摆动显示百分比

一、简介       Qt下利用定时器实现指针指示百分比的钟摆的动态显示效果,可以适用于显示百分比或进度条的进度或时间的刻度值(在圆形进度条上的一种改进)。效果如下:二、详解1、代码(1)Dashb... 查看详情

llvm每日谈之五十五浅谈对pass的错误认知及其原因

Pass作为LLVM的一个重要的组成部分,在LLVMIR层面和LLVMBackend层面都发挥了重要的作用。很多LLVM的使用者容易将Pass理解为LLVMIR层面的analysis和transform,而忽略了Pass在LLVMBackend层面的作用。而实际上,Pass在LLVMBackend层面发... 查看详情

python实例浅谈之五python守护进程和脚本单例运行(代码片段)

一、简介守护进程最重要的特性是后台运行;它必须与其运行前的环境隔离开来,这些环境包括未关闭的文件描述符、控制终端、会话和进程组、工作目录以及文件创建掩码等;它可以在系统启动时从启动脚本/etc/rc.d... 查看详情

浅谈混合开发与android,js数据交互

本文是作者原创,如转载请注明出处!一.概论现在时代已经走过了移动互联网的超级火爆阶段,市场上移动开发人员已经趋于饱和,显然,只会原生APP的开发已不能满足市场的需求,随着H5的兴起与火爆,H5在原生APP中的使用越来越广泛,... 查看详情

llvm每日谈之五十七tablegen(代码片段)

...m-tblgen。通常在build目录下的bin目录里。TableGen主要是帮助开发者开发和维护特定领域的信息记录,方便开发者更好的构建这些信息记录,避免错误。尤其是在面对大量的信息记录的时候,用起来比较方便。TableGen 查看详情

浅谈android,以及android程序的helloworld

前言我本是做服务器开发,有时候工作需要和基于Android、Windows等系统进行协同开发,为了知己知彼,我花了一段时间研究了一下Android。至此,站在一个后台开发者的角度,总结一下Android,以及Android和后... 查看详情

浅谈android组件化(代码片段)

概述软件开发进程也是架构的演进过程,就拿Android来说,从最开始的MVC,MVP,MVVP,再到后来的组件化,插件化,但归根到底一切的一切,都是为了项目更好的维护、迭代,降低开发成本。在一个项目的开发过... 查看详情

浅谈android音视频开发那点事儿,音视频究竟如何入门?

...的丰富性,音视频开发应运而生。其实这一两年关于Android音视频的学习资料已经很多了,包括书籍和网上的一些公开教程。只网络上的资 查看详情

游戏制作大致流程粗谈之四

这次来介绍一下游戏开发团队中的美工角色游戏美术设计师可以简单的分为2D和3D两类,2D即使用位图等二维图形制作游戏;3D则是通过大型的3D游戏引擎制作游戏世界和各种物件的3D模型,并有计算机处理后得到真实感较强的3D图... 查看详情

浅谈一下android开发工程师的未来~

作为曾经炙手可热的Android工程师,现在可能面临着以下的问题:互联网整体环境不乐观Android开发人员众多但岗位减少,导致要求越来越苛刻大前端趋势、跨平台方案频出、小程序肆虐,Android不再是应用开发唯一... 查看详情