Android输入法框架(Input Method Framework,IMF)是Android中非常重要的模块,它分布于三个部分(确切的说,是三个进程),
为了方便描述,后文分别称该三个组件为:client,IME和IMM。
这三个部分需用共同合作才能完成输入法的工作。例如打开一个app,并且一个edit框获取了focus焦点。此时client会通知IMM打开输入法,然后IMM查看当前选中的IME,并调用该IME的start操作。这个简单的开始操作需要三个组件的配合。再比如用户提交了候选词,此时IME需要将候选词告诉client。这里须要IME和client的合作。
因为这三个部分是三个进程,所以它们之间必须通过IPC进行通讯。在Android中,IPC机制是通过binder机制和aidl接口进行通信的。
这些调用关系可以参考下图:
这些接口定义都在java/com/android/internal/view目录下。那这些接口是如何实现的呢?
先看client提供的接口。IInputContext是由同一目录下的IInputConnectionWrapper实现的。正如名字所说,它只是一个wrapper,它把接收到的IPC消息委托给你InputConnection的一个实现。例如对于EditText而言,实现是EditableInputConneciton。
在调用方,IME也不是直接操作IInputContext接口。它会调用实现了InputConnection接口的InputConnectionWrapper(也在前面目录下)。该对象封装了从client传过来的IInputContext实例。
对于IME对client的调用操作,它会经历下面流程(以调用commitText为例,它表示提交候选词):
在IME看来,接口是InputConnection;在client上,实现的也是InputConnection。IInputContext完全被隐藏起来了。所以Android官方文档说IME通过InputConnection接口来操作client。
再看client提供的另外一个接口IInputMethodClient,IMM是直接调用的。IMM的代码就是InputMethodManagerService。在client端,InputMethodManager类中有一个对IInputMethodClient.stub的实现。
对IMM提供的IInputMethodManager接口而言,它是由InputMethodManagerService来实现的。在client端,InputMethodManager的getInstance(是个singleton)会调用ServiceManager.getService(Context.INPUT_METHOD_SERVICE)获取该接口,然后创建InputMethodManager。所以对于client而言,它跟IMM的交互都是通过InputMethodManager来封装完成的,并不需要关心IInputMethodManager接口。对于IME,如果它想操作IMM,也同样通过InputMethodManager。
下面是IME提供的接口。类似于使用InputConnection封装IInputContext,有两个接口InputMethod和InputMethodSession分别对应着了IInputMethod和IInputSession。
对于InputMethod,IInputMethodWrapper实现了IInputMethod.stub。对于收的的IPC请求,都转发给InputMethod实例。一般而言,这个实例是InputMethodService中定义的InputMethodImpl。该实例是InputMethodService的内部类,所以可以操作InputMethodService。对于其客户IMM,InputMethodManagerService会直接调用IInputMethod的方法发起IPC请求。
对于InputMethodSessoin,非常类似,IInputMethodSessionWrapper实现了IInputMethodSession.stub。同样在InputMethodService中有InputMethodSessionImpl实现了InputMethodSession接口,有一个该类型的对象在IInputMethodSessionWrapper中,负责具体处理过来的IPC消息。在client端,InputMethodManager有一个IInputMethodSession mCurMethod对象。开发者只需要调用InputMethodManager,而由InputMethodManager调用IInputMethodSession的IPC操作。
总结一下,无论是client还是IME的开发者,都不需要直接操作aidl接口。在client端,对于IMM和IME的操作都是通过InputMethodManager发起的,用户甚至不用关心这些IPC操作是发给谁的;在IME端,开发者通过InputConnection给client发IPC消息,通过InputMethodManager给IMM发。而在IMM端,虽然是直接操作aidl接口的stub对象,但因为一般开发者不需要改写它,所以也无关紧要。通过这种方式,简化了开发者的跨进程操作。
最后再总结下代码位置:
代码主要就是这四个目录。