歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android4.0 輸入法框架分析

Android4.0 輸入法框架分析

日期:2017/3/1 10:16:48   编辑:Linux編程

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