目錄
1 binderService過程概述:](#title1)
2 對(duì)應(yīng)的流程圖如下](#title2)
3 代碼流程](#title3)
3.1 ActivityThread.handleBindService
3.1.1 AIMS.onBind
3.1.2 AMS.publishService
3.1.3 ActiveServices.publishServiceLocked
3.1.3.1 IMMS.onServiceConnected
3.1.3.1.1 IMMS.MSG_ATTACH_TOKEN
3.1.3.1.2 clearClientSessionLocked
3.1.3.1.3 requestClientSessionLocked
3.1.3.1.4 createSession
3.1.3.1.5 onSessionCreated
3.1.3.1.6 attachNewInputLocked
3.1.3.1.7 onBindMethod
4 總結(jié)
接上一篇文章
上篇文章講了,通過binderserver去啟動(dòng)輸入法服務(wù)時(shí),oncreate的過程
接下來,我們看下,binderserver流程的onBind和onServiceConnected流程
binderService過程概述:
當(dāng)我們調(diào)用bindService()方法的時(shí)候,
會(huì)調(diào)用ContextImpl的bindService()方法,然后獲取到一個(gè)ServiceDispatch.InnerConnection的對(duì)象,可以跨進(jìn)程通信,
調(diào)用ActivityManagerService的bindService()方法,然后調(diào)用bringUpServiceLocked()方法,這個(gè)方法在startService()和bindService()的過程中都會(huì)調(diào)用
執(zhí)行realStartServiceLocked()方法,這個(gè)方法中通過調(diào)用ActivityThread中ApplicationThread的scheduleCreateService()方法,真正執(zhí)行了Service的創(chuàng)建過程,即onCreate()
調(diào)用了requestServiceBindingsLocked()方法,在這個(gè)方法中,會(huì)去調(diào)用ActivityThread中ApplicationThread的scheduleBindService()方法來執(zhí)行Service的bind過程,即onBind()
然后調(diào)用ActivityManagerService的publishService(),然后調(diào)用publishServiceLocked()方法,在這個(gè)方法中會(huì)調(diào)用ServiceDispatch.InnerConnection的connected()方法,最終回調(diào)ServiceConnection的onServiceConnected()方法
對(duì)應(yīng)的流程圖如下
(啟動(dòng)輸入法app服務(wù)_2.png-b5070f-1572529345298-0)]
基于我們對(duì)輸入法相關(guān)流程的關(guān)注點(diǎn),我們重點(diǎn)看下onBind和onServiceConnected流程
我們從上圖中的handleBindService流程開始講起
代碼流程
ActivityThread.handleBindService
ActivityThread.java
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (DEBUG_SERVICE)
Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManager.getService().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to bind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}
主要流程:
1:根據(jù)傳入的intent,調(diào)用onBind方法,返回一個(gè)IInputMethodWrapper對(duì)象
2:使用IInputMethodWrapper binder對(duì)象,調(diào)用publishService方法,最終會(huì)調(diào)用到IMMS的onServiceConnected方法
AIMS.onBind
AbstractInputMethodService.java
@Override
final public IBinder onBind(Intent intent) {
if (mInputMethod == null) {
mInputMethod = onCreateInputMethodInterface();
}
return new IInputMethodWrapper(this, mInputMethod);
}
InputMethodService.java
@Override
public AbstractInputMethodImpl onCreateInputMethodInterface() {
return new InputMethodImpl();
}
AMS.publishService
onBind初始化一個(gè)InputMethodImpl對(duì)象,并將其封裝到IInputMethodWrapper中去
public void publishService(IBinder token, Intent intent, IBinder service) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service token");
}
mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
}
ActiveServices.publishServiceLocked
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
try {
if (r != null) {
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
b.binder = service;
b.requested = true;
b.received = true;
//遍歷服務(wù)ServiceRecord的ConnectionRecord對(duì)象,調(diào)用其持有的IServiceConnection的connected方法
for (int conni=r.connections.size()-1; conni>=0; conni--) {
ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
for (int i=0; i<clist.size(); i++) {
ConnectionRecord c = clist.get(i);
if (!filter.equals(c.binding.intent.intent)) {
if (DEBUG_SERVICE) Slog.v(
TAG_SERVICE, "Not publishing to: " + c);
if (DEBUG_SERVICE) Slog.v(
TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);
if (DEBUG_SERVICE) Slog.v(
TAG_SERVICE, "Published intent: " + intent);
continue;
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
try {
c.conn.connected(r.name, service, false);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + r.name +
" to connection " + c.conn.asBinder() +
" (in " + c.binding.client.processName + ")", e);
}
}
}
}
serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
IMMS.onServiceConnected
IMMS.JAVA
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mMethodMap) {
//mCurIntent 上篇文章有講到,當(dāng)前默認(rèn)輸入法對(duì)應(yīng)的Intent對(duì)象
//校驗(yàn)binder啟動(dòng)的服務(wù)對(duì)應(yīng)的ComponentName和該方法傳遞的name一致性
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
mCurMethod = IInputMethod.Stub.asInterface(service);//獲取應(yīng)用層的IInputMethodWrapper對(duì)象
if (mCurToken == null) {//輸入法應(yīng)用在binderserver的時(shí)候,創(chuàng)建的
Slog.w(TAG, "Service connected without a token!");
unbindCurrentMethodLocked(false);//解綁上次綁定的輸入法服務(wù)
return;
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
//調(diào)用輸入法應(yīng)用端的IInputMethodWrapper$InputMethodImpl:attachToken方法
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
if (mCurClient != null) {//
clearClientSessionLocked(mCurClient);
requestClientSessionLocked(mCurClient);
}
}
}
}
各個(gè)參數(shù)和變量的意義:
service:應(yīng)用層傳遞過來的IInputMethodWrapper對(duì)象,里面封裝了應(yīng)用層創(chuàng)建的InputMethodImpl對(duì)象
mCurToken:IMMS啟動(dòng)輸入法服務(wù)的時(shí)候,創(chuàng)建的一個(gè)Binder,也即是在IMMS.startInputInnerLocked的時(shí)候創(chuàng)建的;unbinder輸入法時(shí),置為null;這個(gè)對(duì)象會(huì)被傳遞到輸入法應(yīng)用,用戶輸入法應(yīng)用和IMMS的一致性校驗(yàn)
mCurMethod:從應(yīng)用層的IInputMethodWrapper對(duì)象
mCurClient:代表當(dāng)前輸入法綁定的應(yīng)用程序端,不過手機(jī)啟動(dòng)過程,因?yàn)檫€沒有窗口被綁定,因此該對(duì)象為null
為了分析問題,我們假設(shè)手機(jī)已經(jīng)啟動(dòng)完畢,并且輸入法已經(jīng)綁定了一個(gè)窗口
IMMS.MSG_ATTACH_TOKEN
IMMS.java
case MSG_ATTACH_TOKEN:
args = (SomeArgs)msg.obj;
try {
if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2);
((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);
} catch (RemoteException e) {
}
args.recycle();
return true;
public void attachToken(IBinder token) {
if (mToken == null) {
mToken = token;
mWindow.setToken(token);
}
}
獲取和保存IMMS創(chuàng)建的額Binder對(duì)象
clearClientSessionLocked
void clearClientSessionLocked(ClientState cs) {
finishSessionLocked(cs.curSession);
cs.curSession = null;//應(yīng)用進(jìn)程第一次創(chuàng)建時(shí),傳遞給IMMS的Session
cs.sessionRequested = false;
}
private void finishSessionLocked(SessionState sessionState) {
if (sessionState != null) {
if (sessionState.session != null) {
try {
sessionState.session.finishSession();
} catch (RemoteException e) {
Slog.w(TAG, "Session failed to close due to remote exception", e);
updateSystemUiLocked(mCurToken, 0 /* vis */, mBackDisposition);
}
sessionState.session = null;
}
if (sessionState.channel != null) {
sessionState.channel.dispose();
sessionState.channel = null;
}
}
}
主要操作是回收ClientState的session對(duì)象
requestClientSessionLocked
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {//檢查是否重復(fù)綁定,或者 clearClientSessionLocked或者unbind輸入法應(yīng)用服務(wù)的時(shí)候,會(huì)置位false
if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
//事件傳遞系統(tǒng),暫不涉及
InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
cs.sessionRequested = true;
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(
MSG_CREATE_SESSION, mCurMethod, channels[1],
new MethodCallback(this, mCurMethod, channels[0])));
}
}
cs.sessionRequested條件的判斷,保證了一個(gè)應(yīng)用進(jìn)程,會(huì)有有一個(gè)輸入法應(yīng)用的session對(duì)象
createSession
調(diào)用IMM輸入法應(yīng)用端
IMMS.java
case MSG_CREATE_SESSION: {
args = (SomeArgs)msg.obj;
IInputMethod method = (IInputMethod)args.arg1;//從應(yīng)用層的IInputMethodWrapper對(duì)象
InputChannel channel = (InputChannel)args.arg2;
try {
method.createSession(channel, (IInputSessionCallback)args.arg3);
} catch (RemoteException e) {
} finally {
// Dispose the channel if the input method is not local to this process
// because the remote proxy will get its own copy when unparceled.
if (channel != null && Binder.isProxy(method)) {
channel.dispose();
}
}
args.recycle();
return true;
- IInputMethodWrapper.createSession;
method 是應(yīng)用層的IInputMethodWrapper對(duì)象;
callback:IMMS端的MethodCallback對(duì)象
@BinderThread
@Override
public void createSession(InputChannel channel, IInputSessionCallback callback) {
//callback:IMMS端的MethodCallback對(duì)象
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
channel, callback));
}
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
//inputMethod為創(chuàng)建IInputMethodWrapper的時(shí)候,創(chuàng)建的InputMethodImpl對(duì)象
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mContext, (InputChannel)args.arg1,
(IInputSessionCallback)args.arg2));
args.recycle();
return;
}
- InputMethodImpl.createSession
@MainThread
public void createSession(SessionCallback callback) {
//callback對(duì)象,InputMethodSessionCallbackWrapper
callback.sessionCreated(onCreateInputMethodSessionInterface());
}
IMS.java
@Override
public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
return new InputMethodSessionImpl();
}
InputMethodSessionCallbackWrapper.java
@Override
public void sessionCreated(InputMethodSession session) {
try {
if (session != null) {
IInputMethodSessionWrapper wrap =
new IInputMethodSessionWrapper(mContext, session, mChannel);
//mCb也即是在IMMS中創(chuàng)建的MethodCallback對(duì)象
mCb.sessionCreated(wrap);
} else {
if (mChannel != null) {
mChannel.dispose();
}
mCb.sessionCreated(null);
}
} catch (RemoteException e) {
}
}
}
mCb也即是在IMMS中創(chuàng)建的MethodCallback對(duì)象
- MethodCallback.sessionCreated
@Override
public void sessionCreated(IInputMethodSession session) {
long ident = Binder.clearCallingIdentity();
try {
mParentIMMS.onSessionCreated(mMethod, session, mChannel);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
最總,該流程在輸入法端轉(zhuǎn)了一圈,將IMS創(chuàng)建的InputMethodSessionImpl對(duì)象,傳遞到了IMMS
onSessionCreated
session:IMS創(chuàng)建的InputMethodSessionImpl對(duì)象
void onSessionCreated(IInputMethod method, IInputMethodSession session,
InputChannel channel) {
synchronized (mMethodMap) {
//mCurMethod,當(dāng)前要連接的輸入法應(yīng)用,由onServiceConnected的IBinder參數(shù)解析而成
if (mCurMethod != null && method != null
&& mCurMethod.asBinder() == method.asBinder()) {//輸入法app端一致性校驗(yàn)
if (mCurClient != null) {//代表應(yīng)用客戶端的對(duì)象,應(yīng)用進(jìn)程創(chuàng)建的時(shí)候創(chuàng)建的
//初始化前先清理,常規(guī)化操作
clearClientSessionLocked(mCurClient);
mCurClient.curSession = new SessionState(mCurClient,
method, session, channel);
InputBindResult res = attachNewInputLocked(
InputMethodClient.START_INPUT_REASON_SESSION_CREATED_BY_IME, true);
if (res.method != null) {
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
MSG_BIND_CLIENT, mCurClient.client, res));
}
return;
}
}
}
// Session abandoned. Close its associated input channel.
channel.dispose();
}
method:IMMSInputMethod
session:IMS端創(chuàng)建的InputMethodSessionImpl對(duì)象
主要流程:
1:校驗(yàn)一致性,保證在session創(chuàng)建階段,輸入法應(yīng)用IMS沒有改變
2:創(chuàng)建SessionState對(duì)象,并存儲(chǔ)代表應(yīng)用端(指的是獲取輸入法的應(yīng)用)的ClientState對(duì)象中
attachNewInputLocked
@GuardedBy("mMethodMap")
@NonNull
InputBindResult attachNewInputLocked(
/* @InputMethodClient.StartInputReason */ final int startInputReason, boolean initial) {
if (!mBoundToMethod) {
//MSG_BIND_INPUT操作
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
mBoundToMethod = true;
}
//創(chuàng)建輸入法相關(guān)的信息
final Binder startInputToken = new Binder();
final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
!initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
mCurSeq);
mStartInputMap.put(startInputToken, info);
mStartInputHistory.addEntry(info);
//SessionState對(duì)象,里面封裝了輸入法端的IInputMethodSession
final SessionState session = mCurClient.curSession;
//調(diào)用MSG_START_INPUT事件
executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
MSG_START_INPUT, mCurInputContextMissingMethods, initial ? 0 : 1 /* restarting */,
startInputToken, session, mCurInputContext, mCurAttribute));
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
}
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
根據(jù)開始章節(jié)的流程圖,這里只講主要流程:
1:調(diào)用AIMS$IInputMethodWrapper的bindInput
傳遞的參數(shù):mCurMethod,IMS端的IMS$InputMethodImpl
mCurClient.binding:每個(gè)進(jìn)程對(duì)應(yīng)一個(gè)該對(duì)象,是在IMMS中創(chuàng)建每個(gè)進(jìn)程的ClientState的時(shí)候創(chuàng)建的
ClientState(IInputMethodClient _client, IInputContext _inputContext,
int _uid, int _pid) {
client = _client;
inputContext = _inputContext;
uid = _uid;
pid = _pid;
binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
}
最總調(diào)用IMS$InputMethodImpl的bindInput方法
@MainThread
@Override
public void bindInput(InputBinding binding) {
mInputBinding = binding;
mInputConnection = binding.getConnection();//為null,,如ClientState構(gòu)造函數(shù)代碼
if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
+ " ic=" + mInputConnection);
//在服務(wù)啟動(dòng)時(shí),IMMS創(chuàng)建的mToken對(duì)象
if (mImm != null && mToken != null) {
mImm.reportFullscreenMode(mToken, mIsFullscreen);//最終調(diào)用到IMMS的reportFullscreenMode
}
initialize();//執(zhí)行具體實(shí)現(xiàn)類的初始化函數(shù)
onBindInput();//執(zhí)行具體實(shí)現(xiàn)類的onBindInput函數(shù)
}
IMMS.JAVA
@Override
public void reportFullscreenMode(IBinder token, boolean fullscreen) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) {
if (!calledWithValidToken(token)) {
return;
}
if (mCurClient != null && mCurClient.client != null) {
mInFullscreenMode = fullscreen;
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, mCurClient));
}
}
}
主要是完成一致性校驗(yàn)工作,然后,又去調(diào)用應(yīng)用端IMM#mClient的reportFullscreenMode
@Override
public void reportFullscreenMode(boolean fullscreen) {
mH.obtainMessage(MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0)
.sendToTarget();
}
case MSG_REPORT_FULLSCREEN_MODE: {
final boolean fullscreen = msg.arg1 != 0;
InputConnection ic = null;
synchronized (mH) {
mFullscreenMode = fullscreen;
if (mServedInputConnectionWrapper != null) {
ic = mServedInputConnectionWrapper.getInputConnection();
}
}
if (ic != null) {
ic.reportFullscreenMode(fullscreen);
}
return;
}
mServedInputConnectionWrapper的創(chuàng)建,是在的IMM$startInputInner中;InputConnection是在創(chuàng)建EditorInfo的時(shí)候創(chuàng)建的,
用于輸入法應(yīng)用和應(yīng)用之間字符的傳遞
EditorInfo tba = new EditorInfo();
// Note: Use Context#getOpPackageName() rather than Context#getPackageName() so that the
// system can verify the consistency between the uid of this process and package name passed
// from here. See comment of Context#getOpPackageName() for details.
tba.packageName = view.getContext().getOpPackageName();
tba.fieldId = view.getId();
InputConnection ic = view.onCreateInputConnection(tba);
if (ic != null) {
!= 0) {
// InputConnection#getHandler() is not implemented.
icHandler = null;
} else {
icHandler = ic.getHandler();
}
servedContext = new ControlledInputConnectionWrapper(
icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this);
} else {
servedContext = null;
missingMethodFlags = 0;
}
mServedInputConnectionWrapper = servedContext;
繞的有點(diǎn)暈,回到attachNewInputLocked方法
1:調(diào)用AIMS$IInputMethodWrapper的startInput
@BinderThread
@Override
public void startInput(IBinder startInputToken, IInputContext inputContext,
@InputConnectionInspector.MissingMethodFlags final int missingMethods,
EditorInfo attribute, boolean restarting) {
if (mIsUnbindIssued == null) {
Log.e(TAG, "startInput must be called after bindInput.");
mIsUnbindIssued = new AtomicBoolean();
}
mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOOO(DO_START_INPUT,
missingMethods, restarting ? 1 : 0, startInputToken, inputContext, attribute,
mIsUnbindIssued));
}
case DO_START_INPUT: {
final SomeArgs args = (SomeArgs) msg.obj;
final int missingMethods = msg.arg1;
final boolean restarting = msg.arg2 != 0;
final IBinder startInputToken = (IBinder) args.arg1;
final IInputContext inputContext = (IInputContext) args.arg2;
final EditorInfo info = (EditorInfo) args.arg3;
final AtomicBoolean isUnbindIssued = (AtomicBoolean) args.arg4;
final InputConnection ic = inputContext != null
? new InputConnectionWrapper(
mTarget, inputContext, missingMethods, isUnbindIssued) : null;
info.makeCompatible(mTargetSdkVersion);
inputMethod.dispatchStartInputWithToken(ic, info, restarting /* restarting */,
startInputToken);
args.recycle();
return;
}
IMS.java
@MainThread
@Override
public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
@NonNull IBinder startInputToken) {
mStartInputToken = startInputToken;
// This needs to be dispatched to interface methods rather than doStartInput().
// Otherwise IME developers who have overridden those interface methods will lose
// notifications.
super.dispatchStartInputWithToken(inputConnection, editorInfo, restarting,
startInputToken);
}
mStartInputToken的意義還沒理解,暫時(shí)不管
3:是否需要顯示輸入法
不同的場(chǎng)景不一樣,在手機(jī)啟動(dòng)場(chǎng)景,因?yàn)橹恍枰疠斎敕ǚ?wù)即可,因此無需顯示輸入法窗口
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
}
4:返回InputBindResult結(jié)果
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
返回的結(jié)果中,包含了輸入法應(yīng)用端的session,當(dāng)前默認(rèn)輸入法的id,mCureSeq輸入法啟動(dòng)的次數(shù)
這個(gè)結(jié)果會(huì)傳遞到普通應(yīng)用端
onBindMethod
IMM.JAVA
@Override
public void onBindMethod(InputBindResult res) {
mH.obtainMessage(MSG_BIND, res).sendToTarget();
}
case MSG_BIND: {
final InputBindResult res = (InputBindResult)msg.obj;
if (DEBUG) {
Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);
}
synchronized (mH) {
//切換輸入法時(shí),會(huì)直接返回
if (mBindSequence < 0 || mBindSequence != res.sequence) {
Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
+ ", given seq=" + res.sequence);
if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
}
return;
}
mRequestUpdateCursorAnchorInfoMonitorMode =
REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
setInputChannelLocked(res.channel);
mCurMethod = res.method;//輸入法應(yīng)用創(chuàng)建的額InputMethodSessionImpl ,用來跟普通應(yīng)用交互
mCurId = res.id;//當(dāng)前綁定的輸入的id
mBindSequence = res.sequence;
}
//調(diào)用startInputInner-->startInputOrWindowGainedFocus
startInputInner(InputMethodClient.START_INPUT_REASON_BOUND_TO_IMMS,
null, 0, 0, 0);
return;
}
也即是說,當(dāng)啟動(dòng)一個(gè)輸入法的時(shí)候,流程會(huì)調(diào)用startInputInner-->startInputOrWindowGainedFocus決定是否彈出輸入法
相關(guān)內(nèi)容,請(qǐng)查看輸入法彈出章節(jié)
總結(jié)
通過以上輸入法應(yīng)用服務(wù)啟動(dòng)過程,我們得到如下各個(gè)對(duì)象關(guān)系

IInputMethodClient:
IMMS使用該接口查找和IMS對(duì)應(yīng)的客戶端應(yīng)用進(jìn)程,并通知應(yīng)用進(jìn)程綁定/解綁輸入法。
oneway interface IInputMethodClient {
void setUsingInputMethod(boolean state);
void onBindMethod(in InputBindResult res);
// unbindReason corresponds to InputMethodClient.UnbindReason.
void onUnbindMethod(int sequence, int unbindReason);
void setActive(boolean active, boolean fullscreen);
void setUserActionNotificationSequenceNumber(int sequenceNumber);
void reportFullscreenMode(boolean fullscreen);
}
InputConnection
負(fù)責(zé)InputMethod進(jìn)程和應(yīng)用進(jìn)程的編輯框的通信,如上屏、查詢光標(biāo)前后字符等
是在IMM$startInputInner的時(shí)候被創(chuàng)建
EditorInfo tba = new EditorInfo();
tba.packageName = view.getContext().getOpPackageName();
tba.fieldId = view.getId();
InputConnection ic = view.onCreateInputConnection(tba);
synchronized (mH) {
...
// Hook 'em up and let 'er rip.
mCurrentTextBoxAttribute = tba;
mServedConnecting = false;
if (mServedInputConnectionWrapper != null) {
mServedInputConnectionWrapper.deactivate();
mServedInputConnectionWrapper = null;
}
ControlledInputConnectionWrapper servedContext;
final int missingMethodFlags;
if (ic != null) {
...
servedContext = new ControlledInputConnectionWrapper(
icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this);
} else {
servedContext = null;
missingMethodFlags = 0;
}
mServedInputConnectionWrapper = servedContext;
IInputMethodSession:
定義了客戶端可以調(diào)用輸入法的相關(guān)方法,如updateCursor, finishInput等
輸入法應(yīng)用端:在createSession階段被AbstractIMS創(chuàng)建,
IMMS服務(wù)端:傳遞到IMMS中,存儲(chǔ)在ClientState的curSession中
應(yīng)用客戶端:
1:在IMM的startInputInner->startInputOrWindowGainedFocus過程中,有IMMS獲取,賦值在mCurMethod變量中
2:在服務(wù)啟動(dòng)階段的IMMattachNewInputLocked,返回到應(yīng)用客戶端,賦值在mCurMethod變量中
interface IInputMethodSession {
void finishInput();
void updateExtractedText(int token, in ExtractedText text);
void updateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd);
void viewClicked(boolean focusChanged);
void updateCursor(in Rect newCursor);
void displayCompletions(in CompletionInfo[] completions);
void appPrivateCommand(String action, in Bundle data);
void toggleSoftInput(int showFlags, int hideFlags);
void finishSession();
void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo);
}
IInputMethod:
負(fù)責(zé)IMS和IMMS的通信,IMMS通知IMS進(jìn)行bindInput,unbindInput,startInput,restartInput,dispatchStartInputWithToken
,hideSoftInput,showSoftInput,changeInputMethodSubtype等生命周期操作
interface IInputMethod {
void attachToken(IBinder token);
void bindInput(in InputBinding binding);
void unbindInput();
void startInput(in IInputContext inputContext, in EditorInfo attribute);
void restartInput(in IInputContext inputContext, in EditorInfo attribute);
void createSession(in InputChannel channel, IInputSessionCallback callback);
void setSessionEnabled(IInputMethodSession session, boolean enabled);
void revokeSession(IInputMethodSession session);
void showSoftInput(int flags, in ResultReceiver resultReceiver);
void hideSoftInput(int flags, in ResultReceiver resultReceiver);
void changeInputMethodSubtype(in InputMethodSubtype subtype);
}