Android輸入法IMMS服務(wù)啟動(dòng)流程(4)(啟動(dòng)IMS應(yīng)用2)

目錄

1 binderService過程概述:](#title1)
2 對(duì)應(yīng)的流程圖如下](#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 總結(jié)

接上一篇文章

上篇文章講了,通過binderserver去啟動(dòng)輸入法服務(wù)時(shí),oncreate的過程

接下來,我們看下,binderserver流程的onBind和onServiceConnected流程

binderService過程概述:

當(dāng)我們調(diào)用bindService()方法的時(shí)候,

  1. 會(huì)調(diào)用ContextImpl的bindService()方法,然后獲取到一個(gè)ServiceDispatch.InnerConnection的對(duì)象,可以跨進(jìn)程通信,

  2. 調(diào)用ActivityManagerService的bindService()方法,然后調(diào)用bringUpServiceLocked()方法,這個(gè)方法在startService()和bindService()的過程中都會(huì)調(diào)用

  3. 執(zhí)行realStartServiceLocked()方法,這個(gè)方法中通過調(diào)用ActivityThread中ApplicationThread的scheduleCreateService()方法,真正執(zhí)行了Service的創(chuàng)建過程,即onCreate()

  4. 調(diào)用了requestServiceBindingsLocked()方法,在這個(gè)方法中,會(huì)去調(diào)用ActivityThread中ApplicationThread的scheduleBindService()方法來執(zhí)行Service的bind過程,即onBind()

  5. 然后調(diào)用ActivityManagerService的publishService(),然后調(diào)用publishServiceLocked()方法,在這個(gè)方法中會(huì)調(diào)用ServiceDispatch.InnerConnection的connected()方法,最終回調(diào)ServiceConnection的onServiceConnected()方法

對(duì)應(yīng)的流程圖如下

(啟動(dòng)輸入法app服務(wù)_2.png-b5070f-1572529345298-0)]

基于我們對(duì)輸入法相關(guān)流程的關(guān)注點(diǎn),我們重點(diǎn)看下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:根據(jù)傳入的intent,調(diào)用onBind方法,返回一個(gè)IInputMethodWrapper對(duì)象
2:使用IInputMethodWrapper binder對(duì)象,調(diào)用publishService方法,最終會(huì)調(diào)用到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初始化一個(gè)InputMethodImpl對(duì)象,并將其封裝到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;
                    //遍歷服務(wù)ServiceRecord的ConnectionRecord對(duì)象,調(diào)用其持有的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 上篇文章有講到,當(dāng)前默認(rèn)輸入法對(duì)應(yīng)的Intent對(duì)象
           //校驗(yàn)binder啟動(dòng)的服務(wù)對(duì)應(yīng)的ComponentName和該方法傳遞的name一致性
            if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
                mCurMethod = IInputMethod.Stub.asInterface(service);//獲取應(yīng)用層的IInputMethodWrapper對(duì)象
                if (mCurToken == null) {//輸入法應(yīng)用在binderserver的時(shí)候,創(chuàng)建的
                    Slog.w(TAG, "Service connected without a token!");
                    unbindCurrentMethodLocked(false);//解綁上次綁定的輸入法服務(wù)
                    return;
                }
                if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
                //調(diào)用輸入法應(yīng)用端的IInputMethodWrapper$InputMethodImpl:attachToken方法
                executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                        MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
                if (mCurClient != null) {//
                    clearClientSessionLocked(mCurClient);
                    requestClientSessionLocked(mCurClient);
                }
            }
        }
    }

各個(gè)參數(shù)和變量的意義:
service:應(yīng)用層傳遞過來的IInputMethodWrapper對(duì)象,里面封裝了應(yīng)用層創(chuàng)建的InputMethodImpl對(duì)象
mCurToken:IMMS啟動(dòng)輸入法服務(wù)的時(shí)候,創(chuàng)建的一個(gè)Binder,也即是在IMMS.startInputInnerLocked的時(shí)候創(chuàng)建的;unbinder輸入法時(shí),置為null;這個(gè)對(duì)象會(huì)被傳遞到輸入法應(yīng)用,用戶輸入法應(yīng)用和IMMS的一致性校驗(yàn)
mCurMethod:從應(yīng)用層的IInputMethodWrapper對(duì)象
mCurClient:代表當(dāng)前輸入法綁定的應(yīng)用程序端,不過手機(jī)啟動(dòng)過程,因?yàn)檫€沒有窗口被綁定,因此該對(duì)象為null
為了分析問題,我們假設(shè)手機(jī)已經(jīng)啟動(dòng)完畢,并且輸入法已經(jīng)綁定了一個(gè)窗口

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創(chuàng)建的額Binder對(duì)象

clearClientSessionLocked
    void clearClientSessionLocked(ClientState cs) {
        finishSessionLocked(cs.curSession);
        cs.curSession = null;//應(yīng)用進(jìn)程第一次創(chuàng)建時(shí),傳遞給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對(duì)象

requestClientSessionLocked
    void requestClientSessionLocked(ClientState cs) {
        if (!cs.sessionRequested) {//檢查是否重復(fù)綁定,或者 clearClientSessionLocked或者unbind輸入法應(yīng)用服務(wù)的時(shí)候,會(huì)置位false
            if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
            //事件傳遞系統(tǒng),暫不涉及
            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條件的判斷,保證了一個(gè)應(yīng)用進(jìn)程,會(huì)有有一個(gè)輸入法應(yīng)用的session對(duì)象

createSession

調(diào)用IMM輸入法應(yīng)用端

IMMS.java
case MSG_CREATE_SESSION: {
                args = (SomeArgs)msg.obj;
                IInputMethod method = (IInputMethod)args.arg1;//從應(yīng)用層的IInputMethodWrapper對(duì)象
                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;
  • IInputMethodWrapper.createSession;
    method 是應(yīng)用層的IInputMethodWrapper對(duì)象;
    callback:IMMS端的MethodCallback對(duì)象

    @BinderThread
    @Override
    public void createSession(InputChannel channel, IInputSessionCallback callback) {
        //callback:IMMS端的MethodCallback對(duì)象
        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
                channel, callback));
    }

    case DO_CREATE_SESSION: {
        SomeArgs args = (SomeArgs)msg.obj;
        //inputMethod為創(chuàng)建IInputMethodWrapper的時(shí)候,創(chuàng)建的InputMethodImpl對(duì)象
        inputMethod.createSession(new InputMethodSessionCallbackWrapper(
                        mContext, (InputChannel)args.arg1,
                        (IInputSessionCallback)args.arg2));
        args.recycle();
        return;
    }

  • InputMethodImpl.createSession
        @MainThread
        public void createSession(SessionCallback callback) {
            //callback對(duì)象,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中創(chuàng)建的MethodCallback對(duì)象
                    mCb.sessionCreated(wrap);
                } else {
                    if (mChannel != null) {
                        mChannel.dispose();
                    }
                    mCb.sessionCreated(null);
                }
            } catch (RemoteException e) {
            }
        }
    }

mCb也即是在IMMS中創(chuàng)建的MethodCallback對(duì)象

  • MethodCallback.sessionCreated
        @Override
        public void sessionCreated(IInputMethodSession session) {
            long ident = Binder.clearCallingIdentity();
            try {
                mParentIMMS.onSessionCreated(mMethod, session, mChannel);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

最總,該流程在輸入法端轉(zhuǎn)了一圈,將IMS創(chuàng)建的InputMethodSessionImpl對(duì)象,傳遞到了IMMS

onSessionCreated

session:IMS創(chuàng)建的InputMethodSessionImpl對(duì)象

   void onSessionCreated(IInputMethod method, IInputMethodSession session,
            InputChannel channel) {
        synchronized (mMethodMap) {
            //mCurMethod,當(dāng)前要連接的輸入法應(yīng)用,由onServiceConnected的IBinder參數(shù)解析而成
            if (mCurMethod != null && method != null
                    && mCurMethod.asBinder() == method.asBinder()) {//輸入法app端一致性校驗(yàn)
                if (mCurClient != null) {//代表應(yīng)用客戶端的對(duì)象,應(yīng)用進(jìn)程創(chuàng)建的時(shí)候創(chuàng)建的
                    //初始化前先清理,常規(guī)化操作
                    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端創(chuàng)建的InputMethodSessionImpl對(duì)象

主要流程:
1:校驗(yàn)一致性,保證在session創(chuàng)建階段,輸入法應(yīng)用IMS沒有改變
2:創(chuàng)建SessionState對(duì)象,并存儲(chǔ)代表應(yīng)用端(指的是獲取輸入法的應(yīng)用)的ClientState對(duì)象中

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

        //創(chuàng)建輸入法相關(guān)的信息
        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對(duì)象,里面封裝了輸入法端的IInputMethodSession
        final SessionState session = mCurClient.curSession;
        //調(diào)用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);
    }

根據(jù)開始章節(jié)的流程圖,這里只講主要流程:

1:調(diào)用AIMS$IInputMethodWrapper的bindInput

傳遞的參數(shù):mCurMethod,IMS端的IMS$InputMethodImpl

mCurClient.binding:每個(gè)進(jìn)程對(duì)應(yīng)一個(gè)該對(duì)象,是在IMMS中創(chuàng)建每個(gè)進(jìn)程的ClientState的時(shí)候創(chuàng)建的

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

最總調(diào)用IMS$InputMethodImpl的bindInput方法

      @MainThread
        @Override
        public void bindInput(InputBinding binding) {
            mInputBinding = binding;
            mInputConnection = binding.getConnection();//為null,,如ClientState構(gòu)造函數(shù)代碼
            if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
                    + " ic=" + mInputConnection);
             //在服務(wù)啟動(dòng)時(shí),IMMS創(chuàng)建的mToken對(duì)象
            if (mImm != null && mToken != null) {
                mImm.reportFullscreenMode(mToken, mIsFullscreen);//最終調(diào)用到IMMS的reportFullscreenMode
            }
            initialize();//執(zhí)行具體實(shí)現(xiàn)類的初始化函數(shù)
            onBindInput();//執(zhí)行具體實(shí)現(xiàn)類的onBindInput函數(shù)
        }

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

主要是完成一致性校驗(yàn)工作,然后,又去調(diào)用應(yīng)用端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的創(chuàng)建,是在的IMM$startInputInner中;InputConnection是在創(chuàng)建EditorInfo的時(shí)候創(chuàng)建的,
用于輸入法應(yīng)用和應(yīng)用之間字符的傳遞

        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;

繞的有點(diǎn)暈,回到attachNewInputLocked方法

1:調(diào)用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的意義還沒理解,暫時(shí)不管

3:是否需要顯示輸入法

不同的場(chǎng)景不一樣,在手機(jī)啟動(dòng)場(chǎng)景,因?yàn)橹恍枰疠斎敕ǚ?wù)即可,因此無需顯示輸入法窗口

        if (mShowRequested) {
            if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
            showCurrentInputLocked(getAppShowFlags(), null);
        }

4:返回InputBindResult結(jié)果

        return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
                session.session, (session.channel != null ? session.channel.dup() : null),
                mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);

返回的結(jié)果中,包含了輸入法應(yīng)用端的session,當(dāng)前默認(rèn)輸入法的id,mCureSeq輸入法啟動(dòng)的次數(shù)
這個(gè)結(jié)果會(huì)傳遞到普通應(yīng)用端

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) {
                        //切換輸入法時(shí),會(huì)直接返回
                        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;//輸入法應(yīng)用創(chuàng)建的額InputMethodSessionImpl ,用來跟普通應(yīng)用交互
                        mCurId = res.id;//當(dāng)前綁定的輸入的id
                        mBindSequence = res.sequence;
                    }
                   //調(diào)用startInputInner-->startInputOrWindowGainedFocus
                    startInputInner(InputMethodClient.START_INPUT_REASON_BOUND_TO_IMMS,
                            null, 0, 0, 0);
                    return;
                }

也即是說,當(dāng)啟動(dòng)一個(gè)輸入法的時(shí)候,流程會(huì)調(diào)用startInputInner-->startInputOrWindowGainedFocus決定是否彈出輸入法
相關(guān)內(nèi)容,請(qǐng)查看輸入法彈出章節(jié)

總結(jié)

通過以上輸入法應(yīng)用服務(wù)啟動(dòng)過程,我們得到如下各個(gè)對(duì)象關(guān)系

輸入法#拉起流程#窗口初始化#組件依賴圖.png

IInputMethodClient:

IMMS使用該接口查找和IMS對(duì)應(yīng)的客戶端應(yīng)用進(jìn)程,并通知應(yīng)用進(jìn)程綁定/解綁輸入法。

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

負(fù)責(zé)InputMethod進(jìn)程和應(yīng)用進(jìn)程的編輯框的通信,如上屏、查詢光標(biāo)前后字符等
是在IMM$startInputInner的時(shí)候被創(chuàng)建

        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:

定義了客戶端可以調(diào)用輸入法的相關(guān)方法,如updateCursor, finishInput等

輸入法應(yīng)用端:在createSession階段被AbstractIMS創(chuàng)建,
IMMS服務(wù)端:傳遞到IMMS中,存儲(chǔ)在ClientState的curSession中
應(yīng)用客戶端:
1:在IMM的startInputInner->startInputOrWindowGainedFocus過程中,有IMMS獲取,賦值在mCurMethod變量中
2:在服務(wù)啟動(dòng)階段的IMMMSG_BIND中,由IMMSattachNewInputLocked,返回到應(yīng)用客戶端,賦值在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:

負(fù)責(zé)IMS和IMMS的通信,IMMS通知IMS進(jìn)行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);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容