1.前言
我们知道输入法InputMethodService和三方app分属于不同进程,彼此不知道对方存在,但是他们又在输入过程中,有交互,他们是怎么建联系的,是谁把他们撮合起来的,图1.1已经给出了答案是InputMethodManagerService,整个撮合过程是我们要分析的。在后面讲解中IMMS 就代表InputMethodManagerService输入法管理者,IME代表InputMethodService输入法,WMS 代表WindowManagerService窗口管理者,下面代码分析是基于Android8.1
2.窗口建档备案(IInputMethodClient)
IInputMethodClient是一个Aidl接口,是供InputMethodManagerService调,获取app和传递给app一些参数用的
2.1我们每个窗口对应一个ViewRoot,他负责把Decorview添加到WMS(WindowManagerService)然后显示,添加操作是通过mWindowSession进行的,mWindowSessiond的init初始化过程中就会把IInputMethodClient和IInputContext传给WMS
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
...
final IWindowSession mWindowSession;
...
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
...
}
}
2.2windowManager就是wms的代理端,windowManager调用openSession把InputMethodClient和IInputContext传给wms
public final class WindowManagerGlobal {
...
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
...
}
2.3看看WMS侧干了些什么?是怎么和IMMS(InputMethodManagerService)联系起来了的
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
...
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
...
}
2.4这里就开始从WMS进入了IMMS ,mService.mInputMethodManager就是IMMS,调用他的addClient把IInputMethodClient添加到了IMMS
public class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
...
public Session(WindowManagerService service, IWindowSessionCallback callback,
IInputMethodClient client, IInputContext inputContext) {
...
mService.mInputMethodManager.addClient(client, inputContext,
mUid, mPid);
...
}
...
}
2.5终于进入到了IMMS,他给客户端窗口按client即IInputMethodClient建档,存到了成员变量mClients中,从这里可以看出每个窗口在创建起初,不管有没有Edittext焦点,IMMS就会给每个窗口存一个以client为key的ClientState为值得价值对。这样InputMethodManagerService调用app端窗口这个线就建立好了。
public class InputMethodManagerService extends IInputMethodManager.Stub{
...
@Override
public void addClient(IInputMethodClient client,
IInputContext inputContext, int uid, int pid) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) {
mClients.put(client.asBinder(), new ClientState(client,
inputContext, uid, pid));
}
}
...
static final class ClientState {
final IInputMethodClient client;
final IInputContext inputContext;
final int uid;
final int pid;
final InputBinding binding;
boolean sessionRequested;
SessionState curSession;
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);
}
}
}
3.IMMS绑定IMS(IInputMethodWrapper)
3.1前面一节只是app和IMMS有了联系,这一节是要IMS与IMMS建立联系,当我们启动一个activity既添加一个窗口到WMS或则退出一个activity,都会导致MWS端的窗口排序发生变化,WMS经过计算得出当先谁是聚焦窗口,然后通过远程调用 W的windowFocusChanged,最后调到了onPostWindowFocus方法第30行
ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
static class W extends IWindow.Stub {
...
@Override
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
}
}
...
}
...
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
Message msg = Message.obtain();
msg.what = MSG_WINDOW_FOCUS_CHANGED;
msg.arg1 = hasFocus ? 1 : 0;
msg.arg2 = inTouchMode ? 1 : 0;
mHandler.sendMessage(msg);
}
...
final class ViewRootHandler extends Handler {
@Override
public void handleMessage(Message msg) {
...
case MSG_WINDOW_FOCUS_CHANGED: {
...
imm.onPostWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
...
}
}
}
}
3.2onPostWindowFocus最后调用到60行,我们看到熟悉的InputConnection和EditorInfo就是这时候收集发送到IMMS的
InputMethodManager.java
public final class InputMethodManager {
public void onPostWindowFocus(View rootView, View focusedView,
@SoftInputModeFlags int softInputMode, boolean first, int windowFlags) {
final IInputMethodManager mService;
...
if (startInputInner(InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN,
rootView.getWindowToken(), controlFlags, softInputMode, windowFlags)) {
return;
}
...
}
boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason,
IBinder windowGainingFocus, int controlFlags, int softInputMode,
int windowFlags) {
...
EditorInfo tba = new EditorInfo();
...
tba.packageName = view.getContext().getOpPackageName();
tba.fieldId = view.getId();
InputConnection ic = view.onCreateInputConnection(tba);
...
synchronized (mH) {
...
mCurrentTextBoxAttribute = tba;
mServedConnecting = false;
if (mServedInputConnectionWrapper != null) {
mServedInputConnectionWrapper.deactivate();
mServedInputConnectionWrapper = null;
}
ControlledInputConnectionWrapper servedContext;
final int missingMethodFlags;
if (ic != null) {
mCursorSelStart = tba.initialSelStart;
mCursorSelEnd = tba.initialSelEnd;
mCursorCandStart = -1;
mCursorCandEnd = -1;
mCursorRect.setEmpty();
mCursorAnchorInfo = null;
final Handler icHandler;
missingMethodFlags = InputConnectionInspector.getMissingMethodFlags(ic);
if ((missingMethodFlags & InputConnectionInspector.MissingMethodFlags.GET_HANDLER)
!= 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;
try {
//远程调用到IMMS带tba(EditorInfo),servedContext(InputConnection)过去
final InputBindResult res = mService.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, controlFlags, softInputMode,
windowFlags, tba, servedContext, missingMethodFlags);
...
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
}
return true;
}
}
3.3上面的startInputOrWindowGainedFocus通过Binder远程调用到MMS,被远程调用过来后经过一系列调用最终到了bindCurrentInputMethodService方法,关键代码66行bindServiceAsUser绑定输入法IME
InputMethodManagerService.java
public class InputMethodManagerService extends IInputMethodManager.Stub
implements ServiceConnection, Handler.Callback {
@Override
public InputBindResult startInputOrWindowGainedFocus(final int startInputReason,
IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,final int missingMethods) {
//这里windowToken不为空走第一个
if (windowToken != null) {
//走这
return windowGainedFocus(startInputReason, client, windowToken, controlFlags,
softInputMode, windowFlags, attribute, inputContext, missingMethods);
} else {
return startInput(startInputReason, client, inputContext, missingMethods, attribute,
controlFlags);
}
}
private InputBindResult windowGainedFocus(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
IInputMethodClient client, IBinder windowToken, int controlFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, EditorInfo attribute, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods) {
//根据客户端传来IInputMethodClient获取第2节的备案的ClientState
ClientState cs = mClients.get(client.asBinder());
...
if (attribute != null) {
return startInputUncheckedLocked(cs, inputContext, missingMethods,
attribute, controlFlags, startInputReason);
}
...
}
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
@NonNull EditorInfo attribute, int controlFlags,
/* @InputMethodClient.StartInputReason */ final int startInputReason) {
...
//设置为当前client,后面创建IInputMethodSession使用
mCurClient = cs;
mCurInputContext = inputContext;
...
return startInputInnerLocked();
}
InputBindResult startInputInnerLocked() {
...
mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
mCurIntent.setComponent(info.getComponent());
mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.input_method_binding_label);
mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
if (bindCurrentInputMethodService(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
...
}
}
private boolean bindCurrentInputMethodService(
Intent service, ServiceConnection conn, int flags) {
if (service == null || conn == null) {
Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
return false;
}
return mContext.bindServiceAsUser(service, conn, flags,
new UserHandle(mSettings.getCurrentUserId()));
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mMethodMap) {
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
mCurMethod = IInputMethod.Stub.asInterface(service);
if (mCurToken == null) {
Slog.w(TAG, "Service connected without a token!");
unbindCurrentMethodLocked(false);
return;
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
requestClientSessionLocked(mCurClient);
}
}
}
}
}
3.4绑定之后下面就去了IME里面看到创建IInputMethodWrapper这个Binder,并且返回了它,因为3.3节47行绑定时传的参数ServiceConnection conn是InputMethodManagerService this,所以绑定之后回调会回调到3.3节的62行把IInputMethodWrapper保存在IMMS的成员变量mCurMethod既66行代码,这样图1.1 的4号线也建立了。
public abstract class AbstractInputMethodService extends Service
implements KeyEvent.Callback {
...
@Override
final public IBinder onBind(Intent intent) {
if (mInputMethod == null) {
mInputMethod = onCreateInputMethodInterface();
}
return new IInputMethodWrapper(this, mInputMethod);
}
...
}
4.创建IInputMethodSession
4.1在3.3节绑定完毕后,IMMS中onServiceConnected就拥有了IInputMethodWrapper,在这个函数中有调用了requestClientSessionLocked这个函数,他的作用是创建图1.1的5号链接,咱们分析代码,最终是调用21行method.createSession(...)这里method既3.4节创建的IInputMethodWrapper,这个Binder可以调用到IME
InputMethodManagerService.java
public class InputMethodManagerService extends IInputMethodManager.Stub
implements ServiceConnection, Handler.Callback {
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {
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])));
}
}
@Override
public boolean handleMessage(Message msg) {
...
case MSG_CREATE_SESSION: {
args = (SomeArgs)msg.obj;
IInputMethod method = (IInputMethod)args.arg1;
InputChannel channel = (InputChannel)args.arg2;
...
//关键代码调用到了IME注意这里的IInputSessionCallback就下面的MethodCallback也是一个Bider,发给IME
//IME创建好了IInputMethodSession,通过MethodCallback发送回来,
//为什么要多此一举,找个回调MethodCallback呢?直接返回不好吗?
//这样做是为了防止阻塞,直接会返回存在问题是的IMMS会一直阻塞到这,直到返回,加了回调我只要发过去就得了,你准备好了再发给我
method.createSession(channel, (IInputSessionCallback)args.arg3);
...
}
...
}
private static final class MethodCallback extends IInputSessionCallback.Stub {
private final InputMethodManagerService mParentIMMS;
private final IInputMethod mMethod;
private final InputChannel mChannel;
MethodCallback(InputMethodManagerService imms, IInputMethod method,
InputChannel channel) {
mParentIMMS = imms;
mMethod = method;
mChannel = channel;
}
@Override
public void sessionCreated(IInputMethodSession session) {
long ident = Binder.clearCallingIdentity();
try {
mParentIMMS.onSessionCreated(mMethod, session, mChannel);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
}
4.2经过上一步4.1 27行调用,就会调用到IME中,18行的inputMethod
IInputMethodWrapper.java
class IInputMethodWrapper extends IInputMethod.Stub
implements HandlerCaller.Callback {
@Override
public void createSession(InputChannel channel, IInputSessionCallback callback) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
channel, callback));
}
@Override
public void executeMessage(Message msg) {
InputMethod inputMethod = mInputMethod.get();
switch (msg.what) {
...
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
//关键代码
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mContext, (InputChannel)args.arg1,
(IInputSessionCallback)args.arg2));
args.recycle();
return;
}
...
}
}
4.3第6行createSession的主要功能是创建InputMethodSessionImpl,并传回到IMMS;InputMethodSessionImpl主要作用是,当app的输入框光标边移动了变化了,用来通知IME,21行到27行
AbstractInputMethodService.java
public abstract class AbstractInputMethodService extends Service
implements KeyEvent.Callback {
...
public abstract class AbstractInputMethodImpl implements InputMethod {
public void createSession(SessionCallback callback) {
callback.sessionCreated(onCreateInputMethodSessionInterface());
}
...
}
@Override
public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
return new InputMethodSessionImpl();
}
...
public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
...
//这个是光标移动了通知输入法IME,只展示一常用的方法
public void updateSelection(int oldSelStart, int oldSelEnd,
int newSelStart, int newSelEnd,
int candidatesStart, int candidatesEnd) {
if (!isEnabled()) {
return;
}
InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
newSelStart, newSelEnd, candidatesStart, candidatesEnd);
}
...
}
4.4创建好了InputMethodSessionImpl,通过callback的sessionCreated传递给IMMS 既7行,onSessionCreated只是做了一个Handler转发,需要注意的是mCurClient这个成员变量,是怎么确定的,因为要通过mCurClient把InputBindResult res传递客户端app,要传递给哪个客户端,我们前面3.3 节26行根据当前聚焦窗口就确定mCurClient,这里的mCurClient就是前面2.4节建档传来的client
public class InputMethodManagerService extends IInputMethodManager.Stub
implements ServiceConnection, Handler.Callback {
...
private static final class MethodCallback extends IInputSessionCallback.Stub {
...
@Override
public void sessionCreated(IInputMethodSession session) {
long ident = Binder.clearCallingIdentity();
try {
//这里传入session
mParentIMMS.onSessionCreated(mMethod, session, mChannel);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
void onSessionCreated(IInputMethod method, IInputMethodSession session,
InputChannel channel) {
synchronized (mMethodMap) {
if (mCurMethod != null && method != null
&& mCurMethod.asBinder() == method.asBinder()) {
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
//这里把session赋值给mCurClient.curSession
mCurClient.curSession = new SessionState(mCurClient,
method, session, channel);
//这里具体看attachNewInputLocked
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();
}
InputBindResult attachNewInputLocked(
/* @InputMethodClient.StartInputReason */ final int startInputReason, boolean initial) {
...
if (!mBoundToMethod) {
//给IME传递
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
mBoundToMethod = true;
}
...
//取出session赋值给InputBindResult
final SessionState session = mCurClient.curSession;
...
return new InputBindResult(session.session,
(session.channel != null ? session.channel.dup() : null),
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
@Override
public boolean handleMessage(Message msg) {
...
case MSG_BIND_CLIENT: {
args = (SomeArgs)msg.obj;
IInputMethodClient client = (IInputMethodClient)args.arg1;
InputBindResult res = (InputBindResult)args.arg2;
...
//取出把带有IInputMethodSession的InputBindResult传回客户端app
client.onBindMethod(res);
...
return true;
...
case MSG_BIND_INPUT:
args = (SomeArgs)msg.obj;
try {
((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
} catch (RemoteException e) {
}
args.recycle();
return true;
...
}
...
}
...
}
4.5接下来就到了app端处理了,app接到 InputBindResult res的后续处理上代码,36行res.method就是从IMMS传过来的IInputMethodSession,也就是IMS里创建的既4.3节13行创建的IInputMethodSession,到这里图1.1的5号线路就建立好了
InputMethodManager.java
public final class InputMethodManager {
IInputMethodSession mCurMethod;
...
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
...
@Override
public void onBindMethod(InputBindResult res) {
mH.obtainMessage(MSG_BIND, res).sendToTarget();
}
...
}
class H extends Handler {
...
@Override
public void handleMessage(Message msg) {
case MSG_BIND: {
final InputBindResult res = (InputBindResult)msg.obj;
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);
//这里的res.method就是从IMMS传过来的IInputMethodSession,也就是IMS里创建的既4.3节13行创建的IInputMethodSession
mCurMethod = res.method;
mCurId = res.id;
mBindSequence = res.sequence;
}
...
return;
}
}
}
InputMethodManager.java
public final class InputMethodManager {
IInputMethodSession mCurMethod;
...
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
...
@Override
public void onBindMethod(InputBindResult res) {
mH.obtainMessage(MSG_BIND, res).sendToTarget();
}
...
}
class H extends Handler {
...
@Override
public void handleMessage(Message msg) {
case MSG_BIND: {
final InputBindResult res = (InputBindResult)msg.obj;
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);
//这里的res.method就是从IMMS传过来的IInputMethodSession,也就是IMS里创建的既4.3节13行创建的IInputMethodSession
mCurMethod = res.method;
mCurId = res.id;
mBindSequence = res.sequence;
}
...
return;
}
}
}
4.6回到4.4 28和31行 IME端bindInput的操作
IInputMethodWrapper.java
class IInputMethodWrapper extends IInputMethod.Stub
implements HandlerCaller.Callback {
...
@Override
public void bindInput(InputBinding binding) {
// This IInputContext is guaranteed to implement all the methods.
final int missingMethodFlags = 0;
InputConnection ic = new InputConnectionWrapper(mTarget,
IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags);
InputBinding nu = new InputBinding(ic, binding);
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
}
...
@Override
public void executeMessage(Message msg) {
...
case DO_SET_INPUT_CONTEXT: {
inputMethod.bindInput((InputBinding)msg.obj);
return;
}
...
}
}
4.7在5.4节的第2行最终调到下面第7行,8、9行完成mInputConnection赋值至此6条线路全部建立
public class InputMethodService extends AbstractInputMethodService {
InputBinding mInputBinding;
InputConnection mInputConnection;
...
public class InputMethodImpl extends AbstractInputMethodImpl {
...
public void bindInput(InputBinding binding) {
mInputBinding = binding;
mInputConnection = binding.getConnection();
if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
+ " ic=" + mInputConnection);
if (mImm != null && mToken != null) {
mImm.reportFullscreenMode(mToken, mIsFullscreen);
}
initialize();
onBindInput();
}
}
...
}
5.传递IInputContext
5.1在2.2节的中16行中imm.getInputContext()会创建IInputContext,看代码mIInputContext是在InputMethodManager构造函数中初始化的,第11行传入的参数是mDummyInputConnection,而mDummyInputConnection是BaseInputConnection看到他的mTargetView为null,那么他是不是输入法提交内容时就没法显示了,那么他是如何显示上屏内容的?留疑问继续看看图1.1的6号线是怎么建立的。
public final class InputMethodManager {
final IInputContext mIInputContext;
final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
public IInputContext getInputContext() {
return mIInputContext;
}
InputMethodManager(IInputMethodManager service, Looper looper) {
mService = service;
mMainLooper = looper;
mH = new H(looper);
mIInputContext = new ControlledInputConnectionWrapper(looper,
mDummyInputConnection, this);
}
}
public class BaseInputConnection implements InputConnection {
...
BaseInputConnection(InputMethodManager mgr, boolean fullEditor) {
mIMM = mgr;
mTargetView = null;
mDummyMode = !fullEditor;
}
...
}
5.2 前面讲过第2节窗口建档的结果就是在IMMS 里面存了一个价值对2.5节10行 值为 ClientState(client, inputContext, uid, pid)里面保存的inputContext就是5.1节第11行创建的ControlledInputConnectionWrapper
5.3我们知道最终inputContext要传递给IME,接下我们看看他是如何传递给IME的,IME创建完成InputMethodSession回调在4.4节第17行IMMS的onSessionCreated方法中,然后调用第28行attachNewInputLocked,看看attachNewInputLocked做了什么。第9行开始往IME传递了,第28行IInputMethod就是绑定IME得到的IInputMethodWrapper (具体3.3节74行),bindInput()之后就进入了IME,这里mCurClient.binding具体看建档环节2.5节31行
InputMethodManagerService.java
public class InputMethodManagerService extends IInputMethodManager.Stub
implements ServiceConnection, Handler.Callback {
InputBindResult attachNewInputLocked(
/* @InputMethodClient.StartInputReason */ final int startInputReason, boolean initial) {
...
if (!mBoundToMethod) {
//给IME传递
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
mBoundToMethod = true;
}
...
//取出session赋值给InputBindResult
final SessionState session = mCurClient.curSession;
...
return new InputBindResult(session.session,
(session.channel != null ? session.channel.dup() : null),
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
@Override
public boolean handleMessage(Message msg) {
...
case MSG_BIND_INPUT:
args = (SomeArgs)msg.obj;
try {
((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
} catch (RemoteException e) {
}
args.recycle();
return true;
...
}
}
5.4IME端bindInput的操作
IInputMethodWrapper.java
class IInputMethodWrapper extends IInputMethod.Stub
implements HandlerCaller.Callback {
...
@Override
public void bindInput(InputBinding binding) {
// This IInputContext is guaranteed to implement all the methods.
final int missingMethodFlags = 0;
InputConnection ic = new InputConnectionWrapper(mTarget,
IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags);
InputBinding nu = new InputBinding(ic, binding);
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
}
...
@Override
public void executeMessage(Message msg) {
...
case DO_SET_INPUT_CONTEXT: {
inputMethod.bindInput((InputBinding)msg.obj);
return;
}
...
}
}
5.5 在5.4节的第2行最终调到下面第7行,8、9行完成mInputConnection赋值至此6条线路全部建立
public class InputMethodService extends AbstractInputMethodService {
InputBinding mInputBinding;
InputConnection mInputConnection;
...
public class InputMethodImpl extends AbstractInputMethodImpl {
...
public void bindInput(InputBinding binding) {
mInputBinding = binding;
mInputConnection = binding.getConnection();
if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
+ " ic=" + mInputConnection);
if (mImm != null && mToken != null) {
mImm.reportFullscreenMode(mToken, mIsFullscreen);
}
initialize();
onBindInput();
}
}
...
}
6.总结
1.当我们要显示一个窗口时,首先会把窗口添加到WMS,在添加过程会发生openSession远程IPC,IInputContext和IInputMethodClient就会传递给WMS
2.WMS会把上面俩兄弟添加到IMMS键档,到此图1.1的2号链接建立
3.当我们把窗口添加到WMS,多了一个窗口WMS肯定要计算各窗口z轴值然后排序,如果这个窗口的z值计算出最大既最顶层,那么就WMS会调到当前窗口的windowFocusChanged
4.当前窗口会遍历所用view,计算出哪个view是聚焦view后,通过startInputOrWindowGainedFocus远程调用IMMS
5.在IMMS绑定IMS
6.绑定返回到IMMS的回调方法中,IMS会传过来IInputMethodWrapper,到这里图1.1的4号链接建立
7.紧接着IMMS会利用上面创建好的4号线,通知IME创建IInputMethodSession
8.创建好之后,把IInputMethodSession传到IMMS
9.在把IInputMethodSession返回到客户端app之前,利用IInputMethodWrapper,把建档时候传来的IInputContext,通过bindInput传递给IME这个就是我们经常使用的IC InputConnection了至此图1.1 的6号线路建立好
10.把第8步已经创建好的InputMethodSession,利用之前建档建立好的2线路传回app,图1.1的5号线路建立好,到此时六条线路全部建立好
7 .疑问
1号和3号线路,怎么没有看到建立过程,这与Android服务机制有有关,他们是一个链接,都是InputMethodManager,因为IMMS是公开服务,它注册到ServiceManager服务注册中心,所以通过第5行代码就可以把名字(input_method)给到到服务注册中心获取1号和3号链接,这有点类似中介(IMMS)和房东(IME)和租客(APP)之间的关系,租客和房东不知道彼此,俩者都会通过114(注册中心),找到中介(IMMS)电话,然后在中介的撮合下俩者完成合同签署
public final class InputMethodManager {
final IInputMethodManager mService;
InputMethodManager(Looper looper) throws ServiceNotFoundException {
this(IInputMethodManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE)), looper);
}
InputMethodManager(IInputMethodManager service, Looper looper) {
mService = service;
mMainLooper = looper;
mH = new H(looper);
mIInputContext = new ControlledInputConnectionWrapper(looper,
mDummyInputConnection, this);
}
}
public static final String INPUT_METHOD_SERVICE = "input_method";