首页 / 操作系统 / Linux / Android4.0 输入法框架分析
InputMethodManager.javaInputMethodManager.java中定义一个变量: IInputMethodSession mCurMethod;从表面上看,似乎是远程使用的。我们在后面有这样一个变量:这个是传到InputMethodManagerService中回调使用的。final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { public void onBindMethod(InputBindResult res) {//这里发送一个消息:MSG_BIND mH.sendMessage(mH.obtainMessage(MSG_BIND, res)); }}
class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {case MSG_BIND: {//很明显:从InputMethodManagerService中传递过来的final InputBindResult res = (InputBindResult)msg.obj;
mCurMethod = res.method;}break;……}}}
InputMethodManagerService.javapublic boolean handleMessage(Message msg) {
……//这里进行回调case MSG_BIND_METHOD:args = (HandlerCaller.SomeArgs)msg.obj; try { ((IInputMethodClient)args.arg1).onBindMethod( (InputBindResult)args.arg2); }}
那么,谁发送了这个消息: void onSessionCreated(IInputMethod method, IInputMethodSession session) { synchronized (mMethodMap) { if (mCurMethod != null && method != null && mCurMethod.asBinder() == method.asBinder()) {//mCurClient 代表是当前SoftInput的客户端 if (mCurClient != null) { mCurClient.curSession = new SessionState(mCurClient, method, session); mCurClient.sessionRequested = false; InputBindResult res = attachNewInputLocked(true); if (res.method != null) { executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO( MSG_BIND_METHOD, mCurClient.client, res)); } } } } }mCurClient与curSession 详见:输入法中相关class.doc那么,谁调用这个onSessionCreated将method和session传进来的呢?继续向下:
又是一个远程的方法实现中,调用上面这个方法,strange!!!private static class MethodCallback extends IInputMethodCallback.Stub {……public void sessionCreated(IInputMethodSession session) throws RemoteException { mParentIMMS.onSessionCreated(mMethod, session);}}
没办法,只有继续向下跟踪:这个远程方法给谁用的???这个方法有两处进行调用:startInputUncheckedLocked和serviceConnection时调用:具体都是如下:executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_CREATE_SESSION, mCurMethod, new MethodCallback(mCurMethod, this)));又是通过发送消息MSG_CREATE_SESSION之后才正式回调MethodCallback中的sessionCreated。最终又绕回去了,还是需要知道IInputMethod mCurMethod这个变量是如何初始化的。
我们看到在InputMethodManagerService重载的函数中有如下调用:public void onServiceConnected(ComponentName name, IBinder service) {……if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {mCurMethod = IInputMethod.Stub.asInterface(service);}}
是不是仍然一头雾水?但是不觉得有黎明破晓的激动吗?我们知道:public abstract class AbstractInputMethodService extends Service {……final public IBinder onBind(Intent intent) { if (mInputMethod == null) {//这里创建了InputMethod接口的方法 mInputMethod = onCreateInputMethodInterface(); }//这里创建一个IInputMethodWrapper return new IInputMethodWrapper(this, mInputMethod);}}
它本质上是一个service,但是它是一个抽象类,它的实现必然是由其子类实现的。public class InputMethodService extends AbstractInputMethodService {
}我们回到,InputMethodManagerService中,在建立service时才将service中的回调方法IInputMethod加载进来,这似乎风马牛不相及,慢着:由IInputMethodWrapper 声明可以看出玄机:class IInputMethodWrapper extends IInputMethod.Stub {
}
现在,我们可以确定,InputMethodManagerService中的mCurMethod是由InputMethodService实现并传过来的。虽然,InputMethodService也是继承来的。
回到原来的问题:谁发送MSG_CREATE_SESSION的Message有两个位置会发送:startInputUncheckedLocked和onServiceConnected
先分析startInputUncheckedLocked这个函数:InputBindResult startInputUncheckedLocked(ClientState cs, IInputContext inputContext, EditorInfo attribute, int controlFlags) {
//没有选中任何输入法,返回 if (mCurMethodId == null) { return mNoBinding; }
//输入法需要切换了if (mCurClient != cs) {//将当前的Client输入法解除绑定unbindCurrentClientLocked();}
//如果屏幕是亮着的if (mScreenOn) { try {//将需要切换的输入法设置为活动的 cs.client.setActive(mScreenOn); } catch (RemoteException e) {
}}
//我们开启一个输入法,在数据库中会记录这个输入法的名称,mCurId 是从数据库中读取的名称 if (mCurId != null && mCurId.equals(mCurMethodId)) {if (cs.curSession != null) {//将当前的client端和InputMethodService绑定,并返回包含id、IInputMethodSession等信//息的InputBindResult return attachNewInputLocked( (controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0);}
//若是已经绑定if (mHaveConnection) {if (mCurMethod != null) { if (!cs.sessionRequested) { cs.sessionRequested = true;//发送创建 MSG_CREATE_SESSION 消息 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_CREATE_SESSION, mCurMethod, new MethodCallback(mCurMethod, this))); } return new InputBindResult(null, mCurId, mCurSeq);}}
return startInputInnerLocked();}
我们到handleMessage中看看如何创建Session的case MSG_CREATE_SESSION: args = (HandlerCaller.SomeArgs)msg.obj;try {//这里将MethodCallback的实例传到IInputMethodWrapper中去了 ((IInputMethod)args.arg1).createSession( (IInputMethodCallback)args.arg2); } catch (RemoteException e) { }return true;然后调用InputMethodWrapper中的createSession来创建Session
IInputMethodWrapper.javapublic void createSession(IInputMethodCallback callback) { mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CREATE_SESSION, callback));}
public void executeMessage(Message msg) {case DO_CREATE_SESSION: {//msg.obj是MethodCallback的实例 inputMethod.createSession(new InputMethodSessionCallbackWrapper( mCaller.mContext, (IInputMethodCallback)msg.obj)); return;}}我们知道,InputMethodService继承自AbstractInputMethodService,但是忽略了这个父类中所用到的类:public abstract class AbstractInputMethodImpl implements InputMethod { public void createSession(SessionCallback callback) {//开始调用MethodCallbacks中中的sessionCreated了,那么,传入参数是什么呢? callback.sessionCreated(onCreateInputMethodSessionInterface()); }}它实现了我们需要的方法:createSession
继续向下:InputMethodService.java //这里InputMethodSessionImpl才是真正的InputMethodService的回调方法类 public AbstractInputMethodImpl onCreateInputMethodInterface() { return new InputMethodSessionImpl(); }
//这里实现InputMethodSession中定义的接口如下所示public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl { public void finishInput() {}
public void viewClicked(boolean focusChanged) {} ……
}AbstractInputMethodSessionImpl 实现了InputMethodSession的方法,这就是说IInputMethodSession的实现就是在这里的InputMethodSessionImpl。我们现在总结一下:InputMethodManager 通过 InputMethodManagerService的本地代理访问InputMethodManagerServiceInputMethodManager 通过 IInputMethodSession访问InputMethodServiceInputMethodManagerService通过 IInputMethodClient 回调InputMethodManager InputMethodManagerService 通过 IInputMethodWrapper(mCurMethod) 回调 InputMethodService