input系统学习----数据分发处理

 

参考资料

Android Pointer location功能研究

https://blog.csdn.net/zhangqi6627/article/details/108467242

 

Introduction Android Input of Pointer Location

https://cwgoover.github.io/2016/05/31/android-setting-pointer-location/

 

InputManagerService分析一:IMS的启动与事件传递

https://blog.csdn.net/lilian0118/article/details/28617185

十分钟了解Android触摸事件原理(InputManagerService

https://www.jianshu.com/p/f05d6b05ba17

 

在开发者选项中,打开 Pointer Location 调试功能,可以显示PointerLocationView 来展示点击信息,这表明,系统的点击事件也发给了PointerLocationView,即可以发给不同的界面(应用),input事件的分发流程是怎样的呢?

 

Input事件是通过socket进行传送的,下图是传送示意图,通过数据传送的这个简图,可以让我们忽略掉繁琐的细枝末节,从整理入手去理解input数据分发。

              socket

IMS  <---------------> WMS

  |----------------------> APP 

         socket

Pointer LocationView中的事件获取是WMS中获取到后再分发的,

APP中的事件流程是ims直接通过socket传送给app的,app把socket的fd传给wms,wms又传给IMS,之后IMS就可以和app进行直接通信了。

 

IMS发送input信息到WMS这部分从Pointer LocationView说起,

打开Pointer Location后,走到

frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java

 

private void enablePointerLocation() {

    if (mPointerLocationView != null) {

        return;

    }

 

    mPointerLocationView = new PointerLocationView(mContext);

    mPointerLocationView.setPrintCoords(false);

    final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(

            WindowManager.LayoutParams.MATCH_PARENT,

            WindowManager.LayoutParams.MATCH_PARENT);

    lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;

    lp.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN

            | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE

            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

    lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;

    if (ActivityManager.isHighEndGfx()) {

        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;

        lp.privateFlags |=

                WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;

    }

    lp.format = PixelFormat.TRANSLUCENT;

    lp.setTitle("PointerLocation - display " + getDisplayId());

    lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;

    final WindowManager wm = mContext.getSystemService(WindowManager.class);

    wm.addView(mPointerLocationView, lp);

    mDisplayContent.registerPointerEventListener(mPointerLocationView);

}

 

关键地方是

    wm.addView(mPointerLocationView, lp);

mDisplayContent.registerPointerEventListener(mPointerLocationView);

 

添加了一个view,并且registerPointerEventListener进行了注册,wms的PointerEventDispatcher分发的时候就会把input数据传入。

 

    private WindowManagerService(Context context, PowerManagerService pm,

            DisplayManagerService displayManager, InputManagerService inputManager,

            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {

           

        ......

       

        mInputManager = inputManager; // Must be before createDisplayContentLocked.

 

        mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG));

       

        .....

    }

 

通过monitorInput可以看到,PointerEventDispatcher(mInputManager.monitorInput(TAG));

这一个操作大有文章,把wms和InputManagerService通过InputChannel关联起来了,他们可以进行socketpair通信了。

InputManagerService的

    public InputChannel monitorInput(String inputChannelName) {

        InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);

        nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);

        inputChannels[0].dispose(); // don't need to retain the Java object reference

        return inputChannels[1];

    }

 

WMS中的InputChannel收到socket信息后,会调用到PointerEventDispatcher的dispatchInputEvent方法去分发,这个过程记为1,后续再查看。

PointerEventDispatcher的定义

30public class PointerEventDispatcher extends InputEventReceiver {
31    ArrayList<PointerEventListener> mListeners = new ArrayList<PointerEventListener>();
32    PointerEventListener[] mListenersArray = new PointerEventListener[0];
33
34    public PointerEventDispatcher(InputChannel inputChannel) {
35        super(inputChannel, UiThread.getHandler().getLooper());
36    }
37
38    @Override
39    public void onInputEvent(InputEvent event, int displayId) {
40        try {
41            if (event instanceof MotionEvent
42                    && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
43                final MotionEvent motionEvent = (MotionEvent) event;
44                PointerEventListener[] listeners;
45                synchronized (mListeners) {
46                    if (mListenersArray == null) {
47                        mListenersArray = new PointerEventListener[mListeners.size()];
48                        mListeners.toArray(mListenersArray);
49                    }
50                    listeners = mListenersArray;
51                }
52                for (int i = 0; i < listeners.length; ++i) {
53                    listeners[i].onPointerEvent(motionEvent, displayId);
54                }
55            }
56        } finally {
57            finishInputEvent(event, false);
58        }
59    }
60
61    /**
62     * Add the specified listener to the list.
63     * @param listener The listener to add.
64     */
65    public void registerInputEventListener(PointerEventListener listener) {
66        synchronized (mListeners) {
67            if (mListeners.contains(listener)) {
68                throw new IllegalStateException("registerInputEventListener: trying to register" +
69                        listener + " twice.");
70            }
71            mListeners.add(listener);
72            mListenersArray = null;
73        }
74    }

 

 

InputEventReceiver.java

 

184    private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
185        mSeqMap.put(event.getSequenceNumber(), seq);
186        onInputEvent(event, displayId);
187    }

 

 

而普通app的input交互处理是在创建窗口的时候创建了InputChannel对象,ViewRootImpl.java

 

   public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

 

   mInputChannel = new InputChannel();

 

 

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {

                ...

            requestLayout();

            if ((mWindowAttributes.inputFeatures

                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {

                 <!--创建InputChannel容器-->

                mInputChannel = new InputChannel();

            }

            try {

                mOrigWindowType = mWindowAttributes.type;

                mAttachInfo.mRecomputeGlobalAttributes = true;

                collectViewAttributes();

                <!--添加窗口,并请求开辟Socket Input通信通道-->

                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,

                        getHostVisibility(), mDisplay.getDisplayId(),

                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,

                        mAttachInfo.mOutsets, mInputChannel);

            }...

            <!--监听,开启Input信道-->

            if (mInputChannel != null) {

                if (mInputQueueCallback != null) {

                    mInputQueue = new InputQueue();

                    mInputQueueCallback.onInputQueueCreated(mInputQueue);

                }

                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,

                        Looper.myLooper());

            }

在IWindowSession.aidl定义中 InputChannel是out类型,也就是说需要服务端进行填充,那么接着看服务端WMS如何填充的呢?

public int addWindow(Session session, IWindow client, int seq,

        WindowManager.LayoutParams attrs, int viewVisibility, int displayId,

        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,

        InputChannel outInputChannel) {           

          ...

        if (outInputChannel != null && (attrs.inputFeatures

                & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {

            String name = win.makeInputChannelName();

            <!--关键点1创建通信信道 -->

            InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);

            <!--本地用-->

            win.setInputChannel(inputChannels[0]);

            <!--APP端用-->

            inputChannels[1].transferTo(outInputChannel);

            <!--注册信道与窗口-->

            mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);

        }

 

通过Wms中转,送到IMS中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值