Android输入法框架系统(上)

来源:互联网 发布:淘宝怎么关闭私人定制 编辑:程序博客网 时间:2024/04/28 01:01

          输入法,就是用来输入字符(包括英文,俄文,中文)的工具。输入法你可以看成是一种字符发生器,它将输入数据触摸事件或者按键事件转化为其他更丰富的字符。在PC时代,输入法的原始输入来自实体键盘,鼠标,然后输入法将这些事件对应的ASCII码转换为俄文,中文,当然如果是英文是不需要转换,直接发送即可。而在Android系统里,由于输入法dialog永远没法成为焦点window,所以输入法永远没法获取到按键事件,也就是说输入法的输入数据只能来自触摸事件,输入法显示出键盘(大家称之为软键盘),用户点击键盘UI, 然后输入法将触摸事件所在位置的字符当做原始字符输入,最后组装成更为丰富的字符(多个字符组成拼音,然后转化为中文),然后就是发送到对应的程序。

        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(hasWindowFocus);                    }                    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">     }
     }
} @Override public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() { //sesion的真正实现 return new InputMethodSessionImpl(); } //然后回到了IMMS void onSessionCreated(IInputMethod method, IInputMethodSession session, InputChannel channel) { synchronized (mMethodMap) { if (mCurMethod != null && method != null && mCurMethod.asBinder() == method.asBinder()) { if (mCurClient != null) { //将session相关的数据封装到SessionState对象里 mCurClient.curSession = new SessionState(mCurClient, method, session, channel); //这个会开始真正的绑定 InputBindResult res = attachNewInputLocked(true); return; } } } }

传递输入法接口给程序

    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来显示输入法。


程序焦点获取事件导致输入法显示

       请查看输入法框架下篇

输入法响应显示请求

       请查看输入法框架下篇

用户单击输入框View导致输入法显示

       请查看输入法框架下篇

输入法传递输入文本信息给view

       请查看输入法框架下篇

/********************************

* 本文来自博客  “爱踢门”

* 转载请标明出处:http://blog.csdn.net/itleaks

******************************************/


0 0