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