android back键后的输入法的流程分析

本文详细解析了Android系统中Back按键的处理流程,从ViewRootImpl的deliverKeyEvent方法开始,逐步深入到InputMethodManager如何通过IInputMethodSession与InputMethodService交互,直至最终处理Back按键的动作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



标题的意思主要想说明:InputMethodManager  通过 IInputMethodSession访问InputMethodService  


先来看按键,back的原始发源地,代码如下:

    private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
        if (ViewDebug.DEBUG_LATENCY) {
            mInputEventDeliverTimeNanos = System.nanoTime();
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 0);
        }

        // If there is no view, then the event will not be handled.
        if (mView == null || !mAdded) {
            finishKeyEvent(event, sendDone, false);
            return;
        }

        if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);

        // Perform predispatching before the IME.
        if (mView.dispatchKeyEventPreIme(event)) {
            finishKeyEvent(event, sendDone, true);
            return;
        }

        // Dispatch to the IME before propagating down the view hierarchy.
        // The IME will eventually call back into handleFinishedEvent.
        if (mLastWasImTarget) {
            InputMethodManager imm = InputMethodManager.peekInstance();
            if (imm != null) {
                int seq = enqueuePendingEvent(event, sendDone);
                if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
                        + seq + " event=" + event);
                Log.d("PateoInputMethod","ViewRootImpl class deliverKeyEvent method InputMethodManager.dispatchKeyEvent");
                imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
                return;
            }
        }

从上面看到调用了InputMethodManager的dispatchKeyEvent方法,相应的日志输出如下:

01-01 10:48:36.010 D/PateoInputMethod( 2208): ViewRootImpl class deliverKeyEvent method InputMethodManager.dispatchKeyEvent

进入该方法,代码如下:

    public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
            IInputMethodCallback callback) {
        synchronized (mH) {
            if (DEBUG) Log.d(TAG,"InputMethodManager class " +  "dispatchKeyEvent");
    
            if (mCurMethod == null) {
                try {
                    callback.finishedEvent(seq, false);
                } catch (RemoteException e) {
                }
                return;
            }
    
            if (key.getAction() == KeyEvent.ACTION_DOWN
                    && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
                showInputMethodPicker();
                try {
                    callback.finishedEvent(seq, true);
                } catch (RemoteException e) {
                }
                return;
            }
            try {
                if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "DISPATCH KEY: " + mCurMethod);
           	 Log.d(TAG,"InputMethodManager class " + " dispatchKeyEvent method, IInputMethodSession mCurMethod dispatchKeyEvent");
                mCurMethod.dispatchKeyEvent(seq, key, callback);
            } catch (RemoteException e) {
                Log.w(TAG,"InputMethodManager class " +  "IME died: " + mCurId + " dropping: " + key, e);
                try {
                    callback.finishedEvent(seq, false);
                } catch (RemoteException ex) {
                }
            }
        }
    }

相应的日志如下:

01-01 10:48:36.010 D/PateoInputMethod( 2208): InputMethodManager class dispatchKeyEvent
01-01 10:48:36.010 V/PateoInputMethod( 2208): InputMethodManager class DISPATCH KEY: com.android.internal.view.IInputMethodSession$Stub$Proxy@420ce8e0
01-01 10:48:36.010 D/PateoInputMethod( 2208): InputMethodManager class  dispatchKeyEvent method, IInputMethodSession mCurMethod dispatchKeyEvent

从日志来看调用了如下方法:

 mCurMethod.dispatchKeyEvent(seq, key, callback);


好吧,我们来看一下mCurMethod的定义:

    /**
     * The actual instance of the method to make calls on it.
     */
    IInputMethodSession mCurMethod;
那么这个mCurMethod什么时候初始化的呢?见下面:

mCurMethod = res.method

InputBindResult res;

我们来看看这个InputBindResult,代码如下:

public final class InputBindResult implements Parcelable {
    static final String TAG = "InputBindResult";
    
    /**
     * The input method service.
     */
    public final IInputMethodSession method;
    

我们再来看一下,我grep的结果:

pateo@pateo-B86N53X:/work/project/a1001eh/frameworks/base$ grep -r "IInputMethodSession.Stub" ./
./core/java/android/inputmethodservice/IInputMethodSessionWrapper.java:class IInputMethodSessionWrapper extends IInputMethodSession.Stub
./core/java/com/android/internal/view/InputBindResult.java:        method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());

原来mCurMethod是在InputBindResult的构造方法里面被赋值的,代码如下:

   InputBindResult(Parcel source) {
        method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());
        id = source.readString();
        sequence = source.readInt();
    }

从上面我们还能看出IInputMethodSessionWrapper extends IInputMethodSession.Stub,好吧,我们回到如下的方法进入:

mCurMethod.dispatchKeyEvent(seq, key, callback);
即为如下的IInputMethodSessionWrapper中的dispatchKeyEvent方法,代码如下:

    public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) {
        mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_KEY_EVENT, seq,
                event, callback));
    }

            case DO_DISPATCH_KEY_EVENT: {
                HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
                mInputMethodSession.dispatchKeyEvent(msg.arg1,
                        (KeyEvent)args.arg1,
                        new InputMethodEventCallbackWrapper(
                                (IInputMethodCallback)args.arg2));
                mCaller.recycleArgs(args);
                return;
            }

这里的mInputMethodSession怎么被赋值的呢?

    public IInputMethodSessionWrapper(Context context,
            InputMethodSession inputMethodSession) {
        mCaller = new HandlerCaller(context, this);
        mInputMethodSession = inputMethodSession;
    }

继续跟踪,上面的方法在哪里被调用:

   static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
        final Context mContext;
        final IInputMethodCallback mCb;
        InputMethodSessionCallbackWrapper(Context context, IInputMethodCallback cb) {
            mContext = context;
            mCb = cb;
        }
        public void sessionCreated(InputMethodSession session) {
            try {
                if (session != null) {
                    IInputMethodSessionWrapper wrap =
                            new IInputMethodSessionWrapper(mContext, session);
                    mCb.sessionCreated(wrap);
                } else {
                    mCb.sessionCreated(null);
                }
            } catch (RemoteException e) {
            }
        }
    }

public abstract class AbstractInputMethodService extends Service
        implements KeyEvent.Callback {
    private InputMethod mInputMethod;
    
    final KeyEvent.DispatcherState mDispatcherState
            = new KeyEvent.DispatcherState();

    /**
     * Base class for derived classes to implement their {@link InputMethod}
     * interface.  This takes care of basic maintenance of the input method,
     * but most behavior must be implemented in a derived class.
     */
    public abstract class AbstractInputMethodImpl implements InputMethod {
        /**
         * Instantiate a new client session for the input method, by calling
         * back to {@link AbstractInputMethodService#onCreateInputMethodSessionInterface()
         * AbstractInputMethodService.onCreateInputMethodSessionInterface()}.
         */
        public void createSession(SessionCallback callback) {
            callback.sessionCreated(onCreateInputMethodSessionInterface());
        }

    /**
     * Called by the framework when a new InputMethodSession interface is
     * needed for a new client of the input method.
     */
    public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();

   public class InputMethodImpl extends AbstractInputMethodImpl {

    @Override
    public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
        return new InputMethodSessionImpl();
    }

好吧,我们回到msg里面的方法,上面的AbstractInputMethodService类中有如下代码:

    public abstract class AbstractInputMethodSessionImpl implements InputMethodSession {

最最重要的代码如下:

       

    public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
        而InputMethodSessionImpl这个类是在InputMethodService类中的,所以可以直接调用InputMethodService中的方法,如下展示了给你看下提供了大致几个方法:
    public class InputMethodSessionImpl extends AbstractInputMethodSessionImpl {
        public void finishInput() {
            if (!isEnabled()) {
                return;
            }
            if (DEBUG) Log.v(TAG,"InputMethodService class" +  "finishInput() in " + this);
            doFinishInput();
        }

        /**
         * Call {@link InputMethodService#onDisplayCompletions
         * InputMethodService.onDisplayCompletions()}.
         */
        public void displayCompletions(CompletionInfo[] completions) {
            if (!isEnabled()) {
                return;
            }
            mCurCompletions = completions;
            onDisplayCompletions(completions);
        }
        
        /**
         * Call {@link InputMethodService#onUpdateExtractedText
         * InputMethodService.onUpdateExtractedText()}.
         */
        public void updateExtractedText(int token, ExtractedText text) {
            if (!isEnabled()) {
                return;
            }
            onUpdateExtractedText(token, text);
        }
        
        /**
         * Call {@link InputMethodService#onUpdateSelection
         * InputMethodService.onUpdateSelection()}.
         */
        public void updateSelection(int oldSelStart, int oldSelEnd,
                int newSelStart, int newSelEnd,
                int candidatesStart, int candidatesEnd) {
            if (!isEnabled()) {
                return;
            }
            InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
                    newSelStart, newSelEnd, candidatesStart, candidatesEnd);
        }

        @Override
        public void viewClicked(boolean focusChanged) {
        	Log.d(TAG, "viewClicked focusChanged=" + focusChanged);
            if (!isEnabled()) {
            	Log.d(TAG, "viewClicked isEnabled false");
                return;
            }
            InputMethodService.this.onViewClicked(focusChanged);
        }

        /**
         * Call {@link InputMethodService#onUpdateCursor
         * InputMethodService.onUpdateCursor()}.
         */
        public void updateCursor(Rect newCursor) {
            if (!isEnabled()) {
                return;
            }
            InputMethodService.this.onUpdateCursor(newCursor);
        }
        
        /**
         * Call {@link InputMethodService#onAppPrivateCommand
         * InputMethodService.onAppPrivateCommand()}.
         */
        public void appPrivateCommand(String action, Bundle data) {
            if (!isEnabled()) {
                return;
            }
            InputMethodService.this.onAppPrivateCommand(action, data);
        }
        
        /**
         * 
         */
        public void toggleSoftInput(int showFlags, int hideFlags) {
            InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
        }
    }

当然,我们的back键调用的方法dispatchKeyEvent没有被子类InputMethodSessionImpl中重写,而是直接在AbstractInputMethodSessionImpl中就被实现了来看看吧      


进入这个类的方法:

      /**
         * Take care of dispatching incoming key events to the appropriate
         * callbacks on the service, and tell the client when this is done.
         */
        public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
            boolean handled = event.dispatch(AbstractInputMethodService.this,
                    mDispatcherState, this);
            if (callback != null) {
                callback.finishedEvent(seq, handled);
            }
        }

进入上面的dispatch方法,进入该方法:

   public final boolean dispatch(Callback receiver, DispatcherState state,
            Object target) {
        switch (mAction) {
            case ACTION_DOWN: {
                mFlags &= ~FLAG_START_TRACKING;
                if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
                        + ": " + this);
                boolean res = receiver.onKeyDown(mKeyCode, this);

这里可以看出,它回调了onKeyDown方法,好吧,我们继续进入AbstractInputMethodService.this的onKeyDown方法:

真实的onKeyDown是由其它的继承的子类实现的,即调用了InputMethodService中的onKeyDown方法,代码如下:

    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            if (handleBack(false)) {
                event.startTracking();
                return true;
            }
            return false;
        }
        return doMovementKey(keyCode, event, MOVEMENT_DOWN);
    }

最终调用了handleBack,代码如下:

    private boolean handleBack(boolean doIt) {
        if (mShowInputRequested) {
            if (isExtractViewShown() && mExtractView instanceof ExtractEditLayout) {
                ExtractEditLayout extractEditLayout = (ExtractEditLayout) mExtractView;
                if (extractEditLayout.isActionModeStarted()) {
                    if (doIt) extractEditLayout.finishActionMode();
                    return true;
                }
            }
            // If the soft input area is shown, back closes it and we
            // consume the back key.
            if (doIt) requestHideSelf(0);

   即近一步调用了

            // If the soft input area is shown, back closes it and we
            // consume the back key.
            if (doIt) requestHideSelf(0);
    public void requestHideSelf(int flags) {
        mImm.hideSoftInputFromInputMethod(mToken, flags);
    }

上面的mImm定义:

    InputMethodManager mImm;

最后还是回到了InputMethodManager,但是从上面我们可以看出:InputMethodManager  通过 IInputMethodSession访问InputMethodService 







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值