目录
1 binderService过程概述:](#title1)
2 对应的流程图如下](#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 总结
接上一篇文章
上篇文章讲了,通过binderserver去启动输入法服务时,oncreate的过程
接下来,我们看下,binderserver流程的onBind和onServiceConnected流程
binderService过程概述:
当我们调用bindService()方法的时候,
-
会调用ContextImpl的bindService()方法,然后获取到一个ServiceDispatch.InnerConnection的对象,可以跨进程通信,
-
调用ActivityManagerService的bindService()方法,然后调用bringUpServiceLocked()方法,这个方法在startService()和bindService()的过程中都会调用
-
执行realStartServiceLocked()方法,这个方法中通过调用ActivityThread中ApplicationThread的scheduleCreateService()方法,真正执行了Service的创建过程,即onCreate()
-
调用了requestServiceBindingsLocked()方法,在这个方法中,会去调用ActivityThread中ApplicationThread的scheduleBindService()方法来执行Service的bind过程,即onBind()
-
然后调用ActivityManagerService的publishService(),然后调用publishServiceLocked()方法,在这个方法中会调用ServiceDispatch.InnerConnection的connected()方法,最终回调ServiceConnection的onServiceConnected()方法
对应的流程图如下
(启动输入法app服务_2.png-b5070f-1572529345298-0)]
基于我们对输入法相关流程的关注点,我们重点看下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:根据传入的intent,调用onBind方法,返回一个IInputMethodWrapper对象
2:使用IInputMethodWrapper binder对象,调用publishService方法,最终会调用到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初始化一个InputMethodImpl对象,并将其封装到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;
//遍历服务ServiceRecord的ConnectionRecord对象,调用其持有的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 上篇文章有讲到,当前默认输入法对应的Intent对象
//校验binder启动的服务对应的ComponentName和该方法传递的name一致性
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
mCurMethod = IInputMethod.Stub.asInterface(service);//获取应用层的IInputMethodWrapper对象
if (mCurToken == null) {//输入法应用在binderserver的时候,创建的
Slog.w(TAG, "Service connected without a token!");
unbindCurrentMethodLocked(false);//解绑上次绑定的输入法服务
return;
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
//调用输入法应用端的IInputMethodWrapper$InputMethodImpl:attachToken方法
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
if (mCurClient != null) {//
clearClientSessionLocked(mCurClient);
requestClientSessionLocked(mCurClient);
}
}
}
}
各个参数和变量的意义:
service:应用层传递过来的IInputMethodWrapper对象,里面封装了应用层创建的InputMethodImpl对象
mCurToken:IMMS启动输入法服务的时候,创建的一个Binder,也即是在IMMS.startInputInnerLocked的时候创建的;unbinder输入法时,置为null;这个对象会被传递到输入法应用,用户输入法应用和IMMS的一致性校验
mCurMethod:从应用层的IInputMethodWrapper对象
mCurClient:代表当前输入法绑定的应用程序端,不过手机启动过程,因为还没有窗口被绑定,因此该对象为null
为了分析问题,我们假设手机已经启动完毕,并且输入法已经绑定了一个窗口
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创建的额Binder对象
clearClientSessionLocked
void clearClientSessionLocked(ClientState cs) {
finishSessionLocked(cs.curSession);
cs.curSession = null;//应用进程第一次创建时,传递给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对象
requestClientSessionLocked
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {//检查是否重复绑定,或者 clearClientSessionLocked或者unbind输入法应用服务的时候,会置位false
if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
//事件传递系统,暂不涉及
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条件的判断,保证了一个应用进程,会有有一个输入法应用的session对象
createSession
调用IMM输入法应用端
IMMS.java
case MSG_CREATE_SESSION: {
args = (SomeArgs)msg.obj;
IInputMethod method = (IInputMethod)args.arg1;//从应用层的IInputMethodWrapper对象
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 是应用层的IInputMethodWrapper对象;
callback:IMMS端的MethodCallback对象
@BinderThread
@Override
public void createSession(InputChannel channel, IInputSessionCallback callback) {
//callback:IMMS端的MethodCallback对象
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
channel, callback));
}
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
//inputMethod为创建IInputMethodWrapper的时候,创建的InputMethodImpl对象
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mContext, (InputChannel)args.arg1,
(IInputSessionCallback)args.arg2));
args.recycle();
return;
}
- InputMethodImpl.createSession
@MainThread
public void createSession(SessionCallback callback) {
//callback对象,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中创建的MethodCallback对象
mCb.sessionCreated(wrap);
} else {
if (mChannel != null) {
mChannel.dispose();
}
mCb.sessionCreated(null);
}
} catch (RemoteException e) {
}
}
}
mCb也即是在IMMS中创建的MethodCallback对象
- MethodCallback.sessionCreated
@Override
public void sessionCreated(IInputMethodSession session) {
long ident = Binder.clearCallingIdentity();
try {
mParentIMMS.onSessionCreated(mMethod, session, mChannel);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
最总,该流程在输入法端转了一圈,将IMS创建的InputMethodSessionImpl对象,传递到了IMMS
onSessionCreated
session:IMS创建的InputMethodSessionImpl对象
void onSessionCreated(IInputMethod method, IInputMethodSession session,
InputChannel channel) {
synchronized (mMethodMap) {
//mCurMethod,当前要连接的输入法应用,由onServiceConnected的IBinder参数解析而成
if (mCurMethod != null && method != null
&& mCurMethod.asBinder() == method.asBinder()) {//输入法app端一致性校验
if (mCurClient != null) {//代表应用客户端的对象,应用进程创建的时候创建的
//初始化前先清理,常规化操作
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端创建的InputMethodSessionImpl对象
主要流程:
1:校验一致性,保证在session创建阶段,输入法应用IMS没有改变
2:创建SessionState对象,并存储代表应用端(指的是获取输入法的应用)的ClientState对象中
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;
}
//创建输入法相关的信息
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对象,里面封装了输入法端的IInputMethodSession
final SessionState session = mCurClient.curSession;
//调用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);
}
根据开始章节的流程图,这里只讲主要流程:
1:调用AIMS$IInputMethodWrapper的bindInput
传递的参数:mCurMethod,IMS端的IMS$InputMethodImpl
mCurClient.binding:每个进程对应一个该对象,是在IMMS中创建每个进程的ClientState的时候创建的
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);
}
最总调用IMS$InputMethodImpl的bindInput方法
@MainThread
@Override
public void bindInput(InputBinding binding) {
mInputBinding = binding;
mInputConnection = binding.getConnection();//为null,,如ClientState构造函数代码
if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
+ " ic=" + mInputConnection);
//在服务启动时,IMMS创建的mToken对象
if (mImm != null && mToken != null) {
mImm.reportFullscreenMode(mToken, mIsFullscreen);//最终调用到IMMS的reportFullscreenMode
}
initialize();//执行具体实现类的初始化函数
onBindInput();//执行具体实现类的onBindInput函数
}
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));
}
}
}
主要是完成一致性校验工作,然后,又去调用应用端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的创建,是在的IMM$startInputInner中;InputConnection是在创建EditorInfo的时候创建的,
用于输入法应用和应用之间字符的传递
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;
绕的有点晕,回到attachNewInputLocked方法
1:调用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的意义还没理解,暂时不管
3:是否需要显示输入法
不同的场景不一样,在手机启动场景,因为只需要拉起输入法服务即可,因此无需显示输入法窗口
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
}
4:返回InputBindResult结果
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
返回的结果中,包含了输入法应用端的session,当前默认输入法的id,mCureSeq输入法启动的次数
这个结果会传递到普通应用端
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) {
//切换输入法时,会直接返回
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;//输入法应用创建的额InputMethodSessionImpl ,用来跟普通应用交互
mCurId = res.id;//当前绑定的输入的id
mBindSequence = res.sequence;
}
//调用startInputInner-->startInputOrWindowGainedFocus
startInputInner(InputMethodClient.START_INPUT_REASON_BOUND_TO_IMMS,
null, 0, 0, 0);
return;
}
也即是说,当启动一个输入法的时候,流程会调用startInputInner-->startInputOrWindowGainedFocus决定是否弹出输入法
相关内容,请查看输入法弹出章节
总结
通过以上输入法应用服务启动过程,我们得到如下各个对象关系

IInputMethodClient:
IMMS使用该接口查找和IMS对应的客户端应用进程,并通知应用进程绑定/解绑输入法。
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
负责InputMethod进程和应用进程的编辑框的通信,如上屏、查询光标前后字符等
是在IMM$startInputInner的时候被创建
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:
定义了客户端可以调用输入法的相关方法,如updateCursor, finishInput等
输入法应用端:在createSession阶段被AbstractIMS创建,
IMMS服务端:传递到IMMS中,存储在ClientState的curSession中
应用客户端:
1:在IMM的startInputInner->startInputOrWindowGainedFocus过程中,有IMMS获取,赋值在mCurMethod变量中
2:在服务启动阶段的IMMattachNewInputLocked,返回到应用客户端,赋值在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:
负责IMS和IMMS的通信,IMMS通知IMS进行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);
}
网友评论