Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / 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