1 Android 输入法框架源码分析总结( 五 )


6.2-1输入法 token的绑定及使用分析

1  Android 输入法框架源码分析总结

文章插图
输入法Window token绑定IMMS在输入法启动完成并回调onServiceConnected时会将一个Window token传递给输入法 。
// InputMethodManagerService.java@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {synchronized (mMethodMap) {if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {mCurMethod = IInputMethod.Stub.asInterface(service);executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(MSG_ATTACH_TOKEN, mCurMethod, mCurToken));if (mCurClient != null) {clearClientSessionLocked(mCurClient);requestClientSessionLocked(mCurClient);}}}}case MSG_ATTACH_TOKEN:args = (SomeArgs)msg.obj;try {//和输入法通信((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);} catch (RemoteException e) {}args.recycle();public class InputMethodService extends AbstractInputMethodService {public class InputMethodImpl extends AbstractInputMethodImpl {public void attachToken(IBinder token) {if (mToken == null) {//保存tokenmToken = token;//这样输入法的window就绑定这个window tokenmWindow.setToken(token);}}}
6.2-2输入法 token使用
由于系统存在多个输入法,所以输入法要和IMMS通信,必须要个机制来标示自己是哪个输入法,这个就是通过上面的输入法Window token来实现的,比如输入法自己关闭自己:
//InputMethodService.java输入法接口public void requestHideSelf(int flags) {//mToken就是上面提到的过程----IMMS传递给输入法的mImm.hideSoftInputFromInputMethod(mToken, flags);}//InputMethodManager.javapublic void hideSoftInputFromInputMethod(IBinder token, int flags) {try {mService.hideMySoftInput(token, flags);} catch (RemoteException e) {throw new RuntimeException(e);}}//IMMS@Overridepublic void hideMySoftInput(IBinder token, int flags) {if (!calledFromValidUser()) {return;}synchronized (mMethodMap) {if (token == null || mCurToken != token) {if (DEBUG) Slog.w(TAG, "Ignoring hideInputMethod of uid "+ Binder.getCallingUid() + " token: " + token);return;}long ident = Binder.clearCallingIdentity();try {hideCurrentInputLocked(flags, null);} finally {Binder.restoreCallingIdentity(ident);}}}
6.3输入法连接会话创建
到此程序和输入法的session就建立了
// InputMethodManagerService.java@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {synchronized (mMethodMap) {if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {// 保存输入法Service 传递过来的 通信接口IInputMethodmCurMethod = 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);// 将刚刚创建的window token传递给输入法service,然后输入用这个token// 创建window,这样IMMS可以用根据这个token找到输入法在IMMS里// 的数据及输入法window在WMS里的数据executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(MSG_ATTACH_TOKEN, mCurMethod, mCurToken));if (mCurClient != null) {clearClientSessionLocked(mCurClient);// 请求为程序和输入法建立一个连接会话,这样client就可以直接和// 输入法通信了requestClientSessionLocked(mCurClient);}}}}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;// InputMethodService.java/*** Concrete implementation of* {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides* all of the standard behavior for an input method.*/public class InputMethodImpl extends AbstractInputMethodImpl {/*** Take care of attaching the given window token provided by the system.*/public void attachToken(IBinder token) {if (mToken == null) {// 保存tokenmToken = token;// 这样输入法的window就绑定这个window tokenmWindow.setToken(token);}}}// InputMethodManagerService.javavoid requestClientSessionLocked(ClientState cs) {if (!cs.sessionRequested) {if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);// 这里又出现了InputChannel对,很面熟吧,在前面几篇文章已经详细分析过// 了,可见它已经成为一种通用的跨平台的数据通信接口了 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])));}}case MSG_CREATE_SESSION: {args = (SomeArgs)msg.obj;IInputMethod method = (IInputMethod)args.arg1;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;}//上面是IMMS端,下面就看IMS输入法端的处理 // IInputMethodWrapper.java@Overridepublic void createSession(InputChannel channel, IInputSessionCallback callback) {mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,channel, callback));}case DO_CREATE_SESSION: {SomeArgs args = (SomeArgs)msg.obj;inputMethod.createSession(new InputMethodSessionCallbackWrapper(mContext, (InputChannel)args.arg1,(IInputSessionCallback)args.arg2));args.recycle();return;}// AbstractInputMethodService.java/*** Base class for derived classes to implement their {@link InputMethod}* interface.This takes care of basic maintenance of the input method,* but most behavior must be implemented in a derived class.*/public abstract class AbstractInputMethodImpl implements InputMethod {/*** Instantiate a new client session for the input method, by calling* back to {@link AbstractInputMethodService#onCreateInputMethodSessionInterface()* AbstractInputMethodService.onCreateInputMethodSessionInterface()}.*/public void createSession(SessionCallback callback) {callback.sessionCreated(onCreateInputMethodSessionInterface());}}// InputMethodManagerService.java@Overridepublic void sessionCreated(IInputMethodSession session) {long ident = Binder.clearCallingIdentity();try {mParentIMMS.onSessionCreated(mMethod, session, mChannel);} finally {Binder.restoreCallingIdentity(ident);}}}