Android OS

Android输入法IMMS服务启动流程(4)(启动IMS应用2

2019-10-31  本文已影响0人  古风子

目录

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()方法的时候,

  1. 会调用ContextImpl的bindService()方法,然后获取到一个ServiceDispatch.InnerConnection的对象,可以跨进程通信,

  2. 调用ActivityManagerService的bindService()方法,然后调用bringUpServiceLocked()方法,这个方法在startService()和bindService()的过程中都会调用

  3. 执行realStartServiceLocked()方法,这个方法中通过调用ActivityThread中ApplicationThread的scheduleCreateService()方法,真正执行了Service的创建过程,即onCreate()

  4. 调用了requestServiceBindingsLocked()方法,在这个方法中,会去调用ActivityThread中ApplicationThread的scheduleBindService()方法来执行Service的bind过程,即onBind()

  5. 然后调用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;

    @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;
    }

        @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对象

        @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:IMMSmCurMethod,也即是mInputMethod : AIMSInputMethod
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决定是否弹出输入法
相关内容,请查看输入法弹出章节

总结

通过以上输入法应用服务启动过程,我们得到如下各个对象关系

输入法#拉起流程#窗口初始化#组件依赖图.png

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:在服务启动阶段的IMMMSG_BIND中,由IMMSattachNewInputLocked,返回到应用客户端,赋值在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);
}
上一篇下一篇

猜你喜欢

热点阅读