Android系统中,输入法可以是可以安装的,也就是说系统可以有多个输入法((sougou输入法,百度输入法),但是只有一个是激活的,当然用户可以切换输入法。同时,输入法是以service的方式运行的,输入法同一时间只能服务一个程序,只有最顶层的可见的程序才能接收到输入法的输入数据。
输入法系统的整个框架如下:
InputMethodManagerService(下文也称IMMS)负责管理系统的所有输入法,包括输入法service(InputMethodService简称IMS)加载及切换。程序获得焦点时,就会通过InputMethodManager向InputMethodManagerService通知自己获得焦点并请求绑定自己到当前输入法上。同时,当程序的某个需要输入法的view比如EditorView获得焦点时就会通过InputMethodManager向InputMethodManagerService请求显示输入法,而这时InputMethodManagerService收到请求后,会将请求的EditText的数据通信接口发送给当前输入法,并请求显输入法。输入法收到请求后,就显示自己的UI dialog,同时保存目标view的数据结构,当用户实现输入后,直接通过view的数据通信接口将字符传递到对应的View。接下来就来分析这些过程。
InputMethodManager创建
每个程序有一个InputMethodManager实例,这个是程序和InputMethodManagerService通信的接口,该实例在ViewRootImpl初始化的时候创建。
- public ViewRootImpl(Context context, Display display) {
- mContext = context;
- mWindowSession = WindowManagerGlobal.getWindowSession();
- }
- public static IWindowSession getWindowSession() {
- synchronized (WindowManagerGlobal.class) {
- if (sWindowSession == null) {
- try {
- //这个进程的InputMethodManager实例就生成了
- InputMethodManager imm = InputMethodManager.getInstance();
- IWindowManager windowManager = getWindowManagerService();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to open window session", e);
- }
- }
- return sWindowSession;
- }
- }
- public static InputMethodManager getInstance() {
- synchronized (InputMethodManager.class) {
- if (sInstance == null) {
- // InputMethodManager其实就是一个Binder service的proxy
- IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
- IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
- sInstance = new InputMethodManager(service, Looper.getMainLooper());
- }
- return sInstance;
- }
- }
程序的Window获得焦点
程序的window获得焦点的时序图如下
系统WindowManagerService更新焦点window
哪个程序获得焦点是由系统决定的,是由WindowManagerService决定的,当系统的window状态发生变化时(比如window新增,删除)就会调用函数updateFocusedWindowLocked来更新焦点window。
- private boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
- //计算焦点window
- WindowState newFocus = computeFocusedWindowLocked();
- if (mCurrentFocus != newFocus) {
- //焦点window发生变化,post一个message来通知程序焦点发生变化了
- mH.removeMessages(H.REPORT_FOCUS_CHANGE);
- mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
- return true;
- }
- return false;
- }
- private WindowState computeFocusedWindowLocked() {
- if (mAnimator.mUniverseBackground != null
- && mAnimator.mUniverseBackground.mWin.canReceiveKeys()) {
- return mAnimator.mUniverseBackground.mWin;
- }
- final int displayCount = mDisplayContents.size();
- for (int i = 0; i < displayCount; i++) {
- final DisplayContent displayContent = mDisplayContents.valueAt(i);
- WindowState win = findFocusedWindowLocked(displayContent);
- if (win != null) {
- return win;
- }
- }
- return null;
- }
- //该函数就是找出最top的可以接收按键事件的window,这个window就获得焦点
- private WindowState findFocusedWindowLocked(DisplayContent displayContent) {
- final WindowList windows = displayContent.getWindowList();
- for (int i = windows.size() - 1; i >= 0; i--) {
- final WindowState win = windows.get(i);
- //是否为activity的window
- AppWindowToken wtoken = win.mAppToken;
- //重要函数,window是否可以获取焦点
- if (!win.canReceiveKeys()) {
- continue;
- }
- // mFocusedApp是最top的activity ,下面逻辑是为了确保焦点window的app
- //必须是焦点程序之上,所以这个逻辑其实并没有多大作用,只是为了检测出
- //错误
- if (wtoken != null && win.mAttrs.type != TYPE_APPLICATION_STARTING &&
- mFocusedApp != null) {
- ArrayList<Task> tasks = displayContent.getTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
- int tokenNdx = tokens.size() - 1;
- for ( ; tokenNdx >= 0; --tokenNdx) {
- final AppWindowToken token = tokens.get(tokenNdx);
- if (wtoken == token) {
- break;
- }
- if (mFocusedApp == token) {
- return null;
- }
- }
- }
- }
- return win;
- }
- return null;
- }
- public final boolean canReceiveKeys() {
- return isVisibleOrAdding()
- && (mViewVisibility == View.VISIBLE)
- && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0);
- }
- //由于输入法的window带有FLAG_NOT_FOCUSABLE, 从上可见其不可能是焦点window
- //接下来系统开始通知程序端哪个window获得了焦点。
- final class H extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case REPORT_FOCUS_CHANGE: {
- WindowState lastFocus;
- WindowState newFocus;
- synchronized(mWindowMap) {
- lastFocus = mLastFocus;
- newFocus = mCurrentFocus;
- if (lastFocus == newFocus) {
- // Focus is not changing, so nothing to do.
- return;
- }
- mLastFocus = newFocus;
- }
- if (newFocus != null) {
- //通知新的焦点程序其获得了焦点
- newFocus.reportFocusChangedSerialized(true, mInTouchMode);
- notifyFocusChanged();
- }
- if (lastFocus != null) {
- //通知老的焦点程序其获得了焦点
- lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
- }
- } break;
- }
- public void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {
- try {
- //这个就是通过Binder告知client其获得或失去了焦点
- mClient.windowFocusChanged(focused, inTouchMode);
- } catch (RemoteException e) {
- }
- }
上面的mClient.windowFocusChanged会调回到ViewRootImpl中的W实例:
程序获得焦点改变事件
- //ViewRootImpl.java
- 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);
- }
- //程序获得焦点会通过调用mView.dispatchWindowFocusChanged和
- //imm.onWindowFocus来通知IMMS焦点信息发生改变,需要更新输入法了
- final class ViewRootHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_WINDOW_FOCUS_CHANGED: {
- if (mAdded) {
- boolean hasWindowFocus = msg.arg1 != 0;
- mAttachInfo.mHasWindowFocus = hasWindowFocus;
- mLastWasImTarget = WindowManager.LayoutParams
- .mayUseInputMethod(mWindowAttributes.flags);
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (mView != null) {
- //调用根view的dispatchWindowFocusChanged函数通知view
- //程序获得焦点
- mView.dispatchWindowFocusChanged(hasWindowFocus);
- mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(
- indowFocus);
- }
- if (hasWindowFocus) {
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- //通知imm该window获得焦点
- imm.onWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode,
- !mHasHadWindowFocus, mWindowAttributes.flags);
- }
- }
- }
- } break;
- }
- //上面的根view就是DecorView,它只是调用父类ViewGroup
- //的dispatchWindowFocusChanged
- //ViewGroup.java
- @Override
- public void dispatchWindowFocusChanged(boolean hasFocus) {
- super.dispatchWindowFocusChanged(hasFocus);
- final int count = mChildrenCount;
- final View[] children = mChildren;
- //让每个子view处理window焦点改变时间
- //但是只有获得焦点的view才会处理这个时间
- for (int i = 0; i < count; i++) {
- children[i].dispatchWindowFocusChanged(hasFocus);
- }
- }
- //View.java
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (!hasWindowFocus) {
- } else if (imm != null && (mPrivateFlags & PFLAG_FOCUSED) != 0) {
- //获得焦点的view通过 InputMethodManager向service通知自己获得焦点
- imm.focusIn(this);
- }
- }
焦点View向IMMS请求绑定输入法
焦点view请求绑定输入法是通过调用InputMethodManager. focusIn实现的
- public void focusIn(View view) {
- synchronized (mH) {
- focusInLocked(view);
- }
- }
- void focusInLocked(View view) {
- //保存焦点view变量
- mNextServedView = view;
- scheduleCheckFocusLocked(view);
- }
- static void scheduleCheckFocusLocked(View view) {
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
- if (viewRootImpl != null) {
- viewRootImpl.dispatchCheckFocus();
- }
- }
- public void dispatchCheckFocus() {
- if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) {
- // This will result in a call to checkFocus() below.
- mHandler.sendEmptyMessage(MSG_CHECK_FOCUS);
- }
- }
- case MSG_CHECK_FOCUS: {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- imm.checkFocus();
- }
- } break;
- public void checkFocus() {
- if (checkFocusNoStartInput(false, true)) {
- startInputInner(null, 0, 0, 0);
- }
- }
- boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,
- int windowFlags) {
- final View view;
- synchronized (mH) {
- //获得上面的焦点view
- view = mServedView;
- }
- EditorInfo tba = new EditorInfo();
- tba.packageName = view.getContext().getPackageName();
- tba.fieldId = view.getId();
- //创建数据通信连接接口,这个会传送到InputMethodService
- //InputMethodService后面就通过这个connection将输入法的字符传递给该view
- InputConnection ic = view.onCreateInputConnection(tba);
- synchronized (mH) {
- mServedInputConnection = ic;
- ControlledInputConnectionWrapper servedContext;
- if (ic != null) {
- mCursorSelStart = tba.initialSelStart;
- mCursorSelEnd = tba.initialSelEnd;
- mCursorCandStart = -1;
- mCursorCandEnd = -1;
- mCursorRect.setEmpty();
- //将InputConnection封装为binder对象,这个是真正可以实现跨进程通
- //信的封装类
- servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);
- }
- mServedInputConnectionWrapper = servedContext;
- try {
- InputBindResult res;
- if (windowGainingFocus != null) {
- //focusIn这个不会走到这条分支
- res = mService.windowGainedFocus(mClient, windowGainingFocus,
- controlFlags, softInputMode, windowFlags,
- tba, servedContext);
- } else {
- //通知InputMethodManagerService,该程序的view获得焦点,IMMS
- //就会将这个view和输入法绑定
- res = mService.startInput(mClient,
- servedContext, tba, controlFlags);
- }
- if (res != null) {
- if (res.id != null) {
- setInputChannelLocked(res.channel);
- mBindSequence = res.sequence;
- //获得了输入法的通信接口
- mCurMethod = res.method;
- mCurId = res.id;
- }
- }
- }
- }
- return true;
- }
IMMS处理view绑定输入法事件
为了讲解整个绑定过程,我们假设此时输入法service还没启动,这个情况下的输入法绑定是最长的,整个过程经历过如下过程:
1) 启动输入法service
2) 绑定输入法window的token
3) 请求输入法为焦点程序创建一个连接会话-
4) 将输入法的接口传递回程序client端
5) 绑定输入法和焦点view
1-4是和程序相关的,而5是和view相关的。所以你可以说1~4是用来绑定程序window和输入法,而5是用来绑定程序view和输入法。
输入法还没启动时,弹出输入法会经过1~5,输入法已经启动,但是焦点window发生变化时会经历3~5,焦点window没有变化,只是改变了焦点view,则只会经历5。整个流程如下:
启动输入法service
- @Override
- public InputBindResult startInput(IInputMethodClient client,
- IInputContext inputContext, EditorInfo attribute, int controlFlags) {
- synchronized (mMethodMap) {
- final long ident = Binder.clearCallingIdentity();
- try {
- return startInputLocked(client, inputContext, attribute, controlFlags);
- }
- }
- }
- InputBindResult startInputLocked(IInputMethodClient client,
- IInputContext inputContext, EditorInfo attribute, int controlFlags) {
- //程序在service端对应的数据结构
- ClientState cs = mClients.get(client.asBinder());
- return startInputUncheckedLocked(cs, inputContext, attribute, controlFlags);
- }
- InputBindResult startInputUncheckedLocked(ClientState cs,
- IInputContext inputContext, EditorInfo attribute, int controlFlags) {
- //如果新程序和当前活动的程序不同
- if (mCurClient != cs) {
- //取消当前活动程序和输入法的绑定
- unbindCurrentClientLocked();
- }
- //将新程序设置为当前活动的程序
- mCurClient = cs;
- mCurInputContext = inputContext;
- mCurAttribute = attribute;
- if (mCurId != null && mCurId.equals(mCurMethodId)) {
- if (cs.curSession != null) {
- //连接已经建立,直接开始绑定
- return attachNewInputLocked(
- (controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0);
- }
- if (mHaveConnection) {
- //输入法的连接是否已经创建,如果已经创建,直接传递给程序client端
- if (mCurMethod != null) {
- requestClientSessionLocked(cs);
- return new InputBindResult(null, null, mCurId, mCurSeq);
- }
- }
- }
- //否则需要启动输入法,并建立连接
- return startInputInnerLocked();
- }
- InputBindResult startInputInnerLocked() {
- InputMethodInfo info = mMethodMap.get(mCurMethodId);
- unbindCurrentMethodLocked(false, true);
- //启动输入法service
- 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, Context.BIND_AUTO_CREATE
- | Context.BIND_NOT_VISIBLE | Context.BIND_SHOWING_UI)) {
- mHaveConnection = true;
- mCurId = info.getId();
- //这个token是给输入法service用来绑定输入法的window的,通过这个token
- //InputMethodManagerService可以很方便的直接管理输入法的window
- mCurToken = new Binder();
- try {
- mIWindowManager.addWindowToken(mCurToken,
- WindowManager.LayoutParams.TYPE_INPUT_METHOD);
- } catch (RemoteException e) {
- }
- return new InputBindResult(null, null, mCurId, mCurSeq);
- }
- return null;
- }
- 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()));
- }
- //输入法启动完成后就在函数onBind 传回一个binder接口
- @Override
- final public IBinder onBind(Intent intent) {
- if (mInputMethod == null) {
- mInputMethod = onCreateInputMethodInterface();
- }
- // IInputMethodWrapper只是一个wrapper,它负责将IMMS的调用转化为message
- //然后在message线程再调用mInputMethod对应的接口
- //这样输入法的处理就是异步的了,因此你说它就是mInputMethod
- return new IInputMethodWrapper(this, mInputMethod);
- }
- @Override
- public AbstractInputMethodImpl onCreateInputMethodInterface() {
- return new InputMethodImpl();
- }
- //由于IMMS是以bindService的方式启动输入法service,所以当输入法service启动完
- //成后它就会回调IMMS的onServiceConnected
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mMethodMap) {
- if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
- //保存输入法service传递过来的通信接口IInputMethod
- mCurMethod = IInputMethod.Stub.asInterface(service);
- //将刚刚创建的window token传递给输入法service,然后输入用这个token
- //创建window,这样IMMS可以用根据这个token找到输入法在IMMS里
- //的数据及输入法window在WMS里的数据
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
- MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
- if (mCurClient != null) {
- //请求为程序和输入法建立一个连接会话,这样client就可以直接和
- //输入法通信了
- requestClientSessionLocked(mCurClient);
- }
- }
- }
- }
输入法Window token的绑定及使用分析
输入法Window token绑定
IMMS在输入法启动完成并回调onServiceConnected时会将一个Window token传递给输入法。
- @Override
- public 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) {
- //保存token
- mToken = token;
- //这样输入法的window就绑定这个window token
- mWindow.setToken(token);
- }
- }
- }
输入法Window token使用
由于系统存在多个输入法,所以输入法要和IMMS通信,必须要个机制来标示自己是哪个输入法,这个就是通过上面的输入法Window token来实现的,比如输入法自己关闭自己:
- //InputMethodService.java输入法接口
- public void requestHideSelf(int flags) {
- //mToken就是上面提到的过程----IMMS传递给输入法的
- mImm.hideSoftInputFromInputMethod(mToken, flags);
- }
- //InputMethodManager.java
- public void hideSoftInputFromInputMethod(IBinder token, int flags) {
- try {
- mService.hideMySoftInput(token, flags);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
- //IMMS
- @Override
- public 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);
- }
- }
- }
输入法连接会话创建
到此程序和输入法的session就建立了
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mMethodMap) {
- if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
- if (mCurClient != null) {
- clearClientSessionLocked(mCurClient);
- requestClientSessionLocked(mCurClient);
- }
- }
- }
- }
- void requestClientSessionLocked(ClientState cs) {
- if (!cs.sessionRequested) {
- //这里又出现了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) {
- }
- //上面是IMMS端,下面就看IMS输入法端的处理
- public abstract class AbstractInputMethodService extends Service
- implements KeyEvent.Callback {
- public abstract class AbstractInputMethodImpl implements InputMethod {
- public void createSession(SessionCallback callback) {
- callback.sessionCreated(onCreateInputMethodSessionInterface());
- }
- pre class="java" name="code"> }
- }
传递输入法接口给程序
- void onSessionCreated(IInputMethod method, IInputMethodSession session,
- InputChannel channel) {
- synchronized (mMethodMap) {
- if (mCurMethod != null && method != null
- && mCurMethod.asBinder() == method.asBinder()) {
- if (mCurClient != null) {
- InputBindResult res = attachNewInputLocked(true);
- if (res.method != null) {
- executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
- MSG_BIND_METHOD, mCurClient.client, res));
- }
- return;
- }
- }
- }
- channel.dispose();
- }
- case MSG_BIND_METHOD: {
- args = (SomeArgs)msg.obj;
- IInputMethodClient client = (IInputMethodClient)args.arg1;
- InputBindResult res = (InputBindResult)args.arg2;
- try {
- //会调回到程序端
- client.onBindMethod(res);
- }
- args.recycle();
- return true;
- }
输入法和view绑定
- //IMMS
- InputBindResult attachNewInputLocked(boolean initial) {
- if (!mBoundToMethod) {
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
- MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
- mBoundToMethod = true;
- }
- final SessionState session = mCurClient.curSession;
- if (initial) {
- executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
- MSG_START_INPUT, session, mCurInputContext, mCurAttribute));
- } else {
- executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
- MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute));
- }
- return new InputBindResult(session.session,
- session.channel != null ? session.channel.dup() : null, mCurId, mCurSeq);
- }
- case MSG_BIND_INPUT:
- args = (SomeArgs)msg.obj;
- try {
- ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
- } catch (RemoteException e) {
- }
- args.recycle();
- return true;
- case MSG_START_INPUT:
- args = (SomeArgs)msg.obj;
- try {
- SessionState session = (SessionState)args.arg1;
- session.method.startInput((IInputContext)args.arg2,
- (EditorInfo)args.arg3);
- } catch (RemoteException e) {
- }
- args.recycle();
- return true;
- //IMS
- @Override
- public void startInput(IInputContext inputContext, EditorInfo attribute) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT,
- inputContext, attribute));
- }
- case DO_START_INPUT: {
- SomeArgs args = (SomeArgs)msg.obj;
- // IInputContext就是输入法和文本输入view的通信接口
- //通过这个接口,输入法能够获取view的信息,也能够直接将文本传
- //送给view
- IInputContext inputContext = (IInputContext)args.arg1;
- InputConnection ic = inputContext != null
- ? new InputConnectionWrapper(inputContext) : null;
- EditorInfo info = (EditorInfo)args.arg2;
- inputMethod.startInput(ic, info);
- args.recycle();
- return;
- }
- public class InputMethodImpl extends AbstractInputMethodImpl {
- public void startInput(InputConnection ic, EditorInfo attribute) {
- doStartInput(ic, attribute, false);
- }
- }
- void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
- if (!restarting) {
- doFinishInput();
- }
- mInputStarted = true;
- mStartedInputConnection = ic;
- mInputEditorInfo = attribute;
- initialize();
- onStartInput(attribute, restarting);
- if (mWindowVisible) {
- if (mShowInputRequested) {
- mInputViewStarted = true;
- //真正的输入法需要在这个接口里实现输入法的内容
- onStartInputView(mInputEditorInfo, restarting);
- startExtractingText(true);
- } else if (mCandidatesVisibility == View.VISIBLE) {
- mCandidatesViewStarted = true;
- onStartCandidatesView(mInputEditorInfo, restarting);
- }
- }
- }
到此焦点view已经通过调用IMMS的startInput和输入法绑定了,但是此时输入法还没有显示。但是系统紧接着会调用windowGainFocus来显示输入法。