美文网首页
Android之input app通信篇(二)客户端业务梳理

Android之input app通信篇(二)客户端业务梳理

作者: 锄禾豆 | 来源:发表于2022-01-27 08:18 被阅读0次
    frameworks/base/core/java/android/view/ViewRootImpl.java
    frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java
    代码:
    7.1
    

    ViewRootImpl
    1.从“Andoid之input app通信篇(一)服务端业务梳理”已经了解到关键信息

    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                                Looper.myLooper());
    

    2.WindowInputEventReceiver怎么监听的并回调处理?
    通过InputChannel承载的socket包裹在WindowInputEventReceiver里面,从而通过native回调回来
    具体如下

    1.WindowInputEventReceiver继承InputEventReceiver
    
        final class WindowInputEventReceiver extends InputEventReceiver {
            ···
            @Override
            public void onInputEvent(InputEvent event) {
                enqueueInputEvent(event, this, 0, true);
            }
            ···
        }
    
    2.通过native回调dispatchInputEvent
    3.对应到WindowInputEventReceiver的方法为onInputEvent
    4.接下来的代码业务流程为:
    onInputEvent --> enqueueInputEvent --> doProcessInputEvents --> deliverInputEvent
    5.重点介绍deliverInputEvent
        private void deliverInputEvent(QueuedInputEvent q) {
            ···
            InputStage stage;
            if (q.shouldSendToSynthesizer()) {
                stage = mSyntheticInputStage;
            } else {
                //q.shouldSkipIme是否忽视输入法
                stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
            }
    
            if (stage != null) {
                stage.deliver(q);
            } else {
                finishInputEvent(q);
            }
        }
    1)这里的stage为InputStage对象
    
    2)q.shouldSkipIme()为按键或触摸事件的分水岭。 关注的一个点:event是按键或触摸。如果是按键event,则为false:
            public boolean shouldSkipIme() {
                if ((mFlags & FLAG_DELIVER_POST_IME) != 0) {
                    return true;
                }
                return mEvent instanceof MotionEvent //MotionEvent是触摸事件
                        && (mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
                            || mEvent.isFromSource(InputDevice.SOURCE_ROTARY_ENCODER));
            }
            
    3)stage调用deliver进行数据传递
    触摸事件的stage=mFirstPostImeInputStage
    按键事件的stage=mFirstInputStage
    

    3-1)分析触摸事件

    1)从EarlyPostImeInputStage开始,以链表的形式,包含下一个stage:NativePostImeInputStage,NativePostImeInputStage包含下一个stage:
    ViewPostImeInputStage
    
    2)EarlyPostImeInputStage.deliver
    deliver --> onProcess --> apply
    
            public final void deliver(QueuedInputEvent q) {
                if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                    forward(q);
                } else if (shouldDropInputEvent(q)) {
                    finish(q, false);
                } else {
                    apply(q, onProcess(q));
                }
            }
    
    EarlyPostImeInputStage.onProcess 结果为FORWARD,这样通过apply --> forward --> onDeliverToNext
            
            protected void onDeliverToNext(QueuedInputEvent q) {
                if (DEBUG_INPUT_STAGES) {
                    Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
                }
                if (mNext != null) {
                    mNext.deliver(q);
                } else {
                    finishInputEvent(q);
                }
            }
    这样就来到mNext(NativePostImeInputStage)的执行流程
    
    NativePostImeInputStage的执行结果(主要mInputQueue=null)仍是执行下一个mNext(ViewPostImeInputStage)
    ViewPostImeInputStage
    deliver --> onProcess --> apply
            @Override
            protected int onProcess(QueuedInputEvent q) {
                if (q.mEvent instanceof KeyEvent) {
                    return processKeyEvent(q);
                } else {
                    final int source = q.mEvent.getSource();
                    if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {//执行这里
                        return processPointerEvent(q);
                    } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                        return processTrackballEvent(q);
                    } else {
                        return processGenericMotionEvent(q);
                    }
                }
            }
            
            private int processPointerEvent(QueuedInputEvent q) {
                ···
                //这里的eventTarget为DecorView
                final View eventTarget =
                        (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
                                mCapturingView : mView;
                ···
                //到DecorView中处理dispatchPointerEvent
                boolean handled = eventTarget.dispatchPointerEvent(event);
                ···
                return handled ? FINISH_HANDLED : FORWARD;
            }
    
    DecorView
        public final boolean dispatchPointerEvent(MotionEvent event) {
            if (event.isTouchEvent()) {//是触摸事件,走dispatchTouchEvent
                return dispatchTouchEvent(event);
            } else {
                return dispatchGenericMotionEvent(event);
            }
        }
        
        public boolean dispatchTouchEvent(MotionEvent ev) {
            //mWindow.getCallback就是Activity本身,因为它实现了接口Window.Callback
            //Activity.attach有:mWindow.setCallback(this);
            //DecorView的mFeatureId = -1
            final Window.Callback cb = mWindow.getCallback();
            //这样就来到Activity.dispatchTouchEvent
            return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                    ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
        }
        
    Activiy.dispatchTouchEvent
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                onUserInteraction();
            }
            if (getWindow().superDispatchTouchEvent(ev)) {
                return true;
            }
            return onTouchEvent(ev);
        }
    

    触摸事件对应到Activity中为dispatchTouchEvent

    3-2)分析按键事件

    1.从NativePreImeInputStage开始分析,以链表的形式,连接的如下
    NativePreImeInputStage --next-> ViewPreImeInputStage --next-> ImeInputStage --next-> EarlyPostImeInputStage --next-> NativePostImeInputStage --next-> ViewPostImeInputStage --next-> SyntheticInputStage
    
    代码流程:deliver --> onProcess --> apply --> forward --> onDeliverToNext --> deliver
    
    NativePreImeInputStage.onProcess --··--> ViewPreImeInputStage.onProcess --··--> ImeInputStage.onProcess
    
    ImeInputStage.onProcess
        final class ImeInputStage extends AsyncInputStage
                implements InputMethodManager.FinishedInputEventCallback {
            ···
            @Override
            protected int onProcess(QueuedInputEvent q) {
                if (mLastWasImTarget && !isInLocalFocusMode()) {
                    InputMethodManager imm = InputMethodManager.peekInstance();
                    if (imm != null) {
                        final InputEvent event = q.mEvent;
                        //发送到InputMethodManager中,回到为FinishedInputEventCallback
                        //结果为DEFER,插入队列中,等待回调到onFinishedInputEvent才使用
                        //流程 app --> ime --> app
                        int result = imm.dispatchInputEvent(event, q, this, mHandler);
                        if (result == InputMethodManager.DISPATCH_HANDLED) {
                            return FINISH_HANDLED;
                        } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
                            // The IME could not handle it, so skip along to the next InputStage
                            return FORWARD;
                        } else {
                            return DEFER; // callback will be invoked later
                        }
                    }
                }
                return FORWARD;
            }
    
            @Override
            public void onFinishedInputEvent(Object token, boolean handled) {
                QueuedInputEvent q = (QueuedInputEvent)token;
                if (handled) {
                    finish(q, true);
                    return;
                }
                forward(q);
            }
        }
        这里我们先关注回调后的业务处理情况,回调过程的实现我们先不关注
    EarlyPostImeInputStage.onProcess --> NativePostImeInputStage.onProcess --> ViewPostImeInputStage.onProcess
    ViewPostImeInputStage.onProcess
        final class ViewPostImeInputStage extends InputStage {
            ···
            @Override
            protected int onProcess(QueuedInputEvent q) {
                if (q.mEvent instanceof KeyEvent) {
                    return processKeyEvent(q);
                } else {
                    ···
                }
            }
            ···
            private int processKeyEvent(QueuedInputEvent q) {
                final KeyEvent event = (KeyEvent)q.mEvent;
    
                // Deliver the key to the view hierarchy.
                // 到DecorView中处理dispatchKeyEvent事件
                if (mView.dispatchKeyEvent(event)) {
                    return FINISH_HANDLED;
                }
                ···
                return FORWARD;
            }
    
    DecorView.dispatchKkeyEvent
        public boolean dispatchKeyEvent(KeyEvent event) {
            ···
            if (!mWindow.isDestroyed()) {
                final Window.Callback cb = mWindow.getCallback();
                //cb为Activity对象,这里就关联到Activity.dispatchKeyEvent
                final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                        : super.dispatchKeyEvent(event);
                if (handled) {
                    return true;
                }
            }
    
            //如果上面没有拦截,则会调用PhoneWindow.onKeyDown/onKeyUp
            //这里面有音量加减键的触发等
            return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
                    : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
        }
    

    (1)按键事件对应到Activity.dispatchKeyEvent
    (2)如果没有分发成功,则调用PhoneWindow.onKeyDown/onKeyUp事件,这里的事件包含了音量的分发

    3-3)IME -- app的回调搭建分析
    代码分析:

    InputMethodManager imm = InputMethodManager.peekInstance();
    int result = imm.dispatchInputEvent(event, q, this, mHandler);
    
    InputMethodManager.dispatchInputEvent
        public int dispatchInputEvent(InputEvent event, Object token,
                FinishedInputEventCallback callback, Handler handler) {
            synchronized (mH) {
                if (mCurMethod != null) {
                    ···
                    //将数据放在PendingEvent中
                    PendingEvent p = obtainPendingEventLocked(
                            event, token, mCurId, callback, handler);
                    if (mMainLooper.isCurrentThread()) {
                        // Already running on the IMM thread so we can send the event immediately.
                        //将p通过sendInputEventOnMainLooperLocked发送出去
                        return sendInputEventOnMainLooperLocked(p);
                    }
                    ···
                }
            }
            return DISPATCH_NOT_HANDLED;
        }
    
        int sendInputEventOnMainLooperLocked(PendingEvent p) {
            if (mCurChannel != null) {
                if (mCurSender == null) {
                    //创建socket通信的处理器ImeInputEventSender
                    mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
                }
    
                final InputEvent event = p.mEvent;
                final int seq = event.getSequenceNumber();
                if (mCurSender.sendInputEvent(seq, event)) {//发送数据到socket服务端
                    mPendingEvents.put(seq, p);//将数据保存在数组中
                    Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
                            mPendingEvents.size());
    
                    //超时机制监听
                    Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
                    msg.setAsynchronous(true);
                    mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
                    return DISPATCH_IN_PROGRESS;
                }
    
                Log.w(TAG, "Unable to send input event to IME: "
                        + mCurId + " dropping: " + event);
            }
            return DISPATCH_NOT_HANDLED;
        }
        
    
    InputMethodManager.ImeInputEventSender接收服务端socket发送的数据
        private final class ImeInputEventSender extends InputEventSender {
            public ImeInputEventSender(InputChannel inputChannel, Looper looper) {
                super(inputChannel, looper);
            }
    
            //native的回调函数
            @Override
            public void onInputEventFinished(int seq, boolean handled) {
                finishedInputEvent(seq, handled, false);
            }
        }
        
        void finishedInputEvent(int seq, boolean handled, boolean timeout) {
            final PendingEvent p;
            synchronized (mH) {
                int index = mPendingEvents.indexOfKey(seq);
                if (index < 0) {
                    return; // spurious, event already finished or timed out
                }
    
                //从数据获取保存在数组中的PendingEvent对象
                p = mPendingEvents.valueAt(index);
                mPendingEvents.removeAt(index);
                Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());
    
                ···
            }
    
            //调用PendingEvent的回调函数
            invokeFinishedInputEventCallback(p, handled);
        }
        
        void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
            p.mHandled = handled;
            if (p.mHandler.getLooper().isCurrentThread()) {
                //执行run
                p.run();
            } else {
                ···
            }
        }
        
        private final class PendingEvent implements Runnable {
            ···
            @Override
            public void run() {
                //执行回调方法,就回到了app
                mCallback.onFinishedInputEvent(mToken, mHandled);
                ···
            }
        }
    

    总结

    1.触摸事件对应到Activity中为dispatchTouchEvent
    2.按键事件对应到Activity.dispatchKeyEvent
    3.触摸事件和按键事件的业务分发流程有差异,但根源都是WindowInputEventReceiver处理
    

    相关文章

      网友评论

          本文标题:Android之input app通信篇(二)客户端业务梳理

          本文链接:https://www.haomeiwen.com/subject/xgfwhrtx.html