歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Android4.0 輸入法框架分析

InputMethodManager.java

InputMethodManager.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.java

public 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.java

public 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的本地代理訪問InputMethodManagerService

InputMethodManager  通過 IInputMethodSession訪問InputMethodService

InputMethodManagerService通過 IInputMethodClient 回調InputMethodManager 

InputMethodManagerService 通過 IInputMethodWrapper(mCurMethod)  回調 InputMethodService

Copyright © Linux教程網 All Rights Reserved