Event distribution principle

1 Overview

In the previous article, the working principle of View analyzed how the interface works after the Activity is started, and how to perform the measurement, layout, and drawing process of the View to display it on the screen. At this time, the interface of the App can already interact. The interaction with the app is often triggered by touching the screen of the mobile phone, which involves the triggering and distribution principles of screen touch events.

2. Distribution process

The triggering and distribution process of the entire event involves a lot of related knowledge points, such as inter-process communication, Binder, Socket and many Native (c++) code related processes. The article mainly analyzes the relevant processes on the Java code, and the processes on the Native will be generally taken.

2.1InputManagerService

The process from touching the mobile phone screen to make the hardware layer driver receive the touch signal to sending the touch event by the Linux kernel layer will not be analyzed here. Let's start with InputManagerService. InputManagerService is started with the startup of the system_server process. If you are interested in the startup of system_server, you can read the article SystemServer Workflow .

public InputManagerService(Context context) {
    //上下文
    this.mContext = context; 
    //接收事件的Handler,运行在线程"android.display"
    this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
    ....
    //绑定Native对象并获取地址mPtr
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    ....
    //注册服务到ServiceManager
    LocalServices.addService(InputManagerInternal.class, new LocalService());
}
复制代码

As long as the construction method is to create a Handler that handles events and bind it to the Looper of the "android.display" thread, DisplayThread is actually a singleton HandlerThread object. Then bind the Native object to obtain the address information of the Native object.

public void start() {
    //绑定Native对象
    nativeStart(mPtr);
    ....
    //注册几种观察者
    registerPointerSpeedSettingObserver();
    registerShowTouchesSettingObserver();
    registerAccessibilityLargePointerSettingObserver();
    mContext.registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //更新设置
            updatePointerSpeedFromSettings();
            updateShowTouchesFromSettings();
            updateAccessibilityLargePointerFromSettings();
        }
    }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
    //更新设置
    updatePointerSpeedFromSettings();
    updateShowTouchesFromSettings();
    updateAccessibilityLargePointerFromSettings();
}
复制代码

可以看到InputManagerService的start方法最核心的地方是跟Native做绑定,整体InputManagerService的工作流程大部分逻辑其实是native层来实现的,包括事件的处理线程、事件管理等方面。这篇文章有详细的流程上的分析Input系统—启动篇,这里需要提一下InputManagerService的registerInputChannel方法,下文中的流程会涉及到这里。

public void registerInputChannel(InputChannel inputChannel,
        InputWindowHandle inputWindowHandle) {
    //绑定InputChannel
    nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
复制代码

2.2ViewRootImpl

事件分发这里又涉及到了ViewRootImpl,可见ViewRootImpl不单单负责View的测量、布局和绘制,触摸事件的分发流程也有参与。

public ViewRootImpl(Context context, Display display) {
    ....
    //触摸事件相关流程的关键代码,获取IWindowSession的代理类
    mWindowSession = WindowManagerGlobal.getWindowSession();
    ....
}
WindowManagerGlobal#getWindowSession
public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                //获取IMS的代理类
                InputMethodManager imm = InputMethodManager.getInstance();
                //获取WMS的代理类
                IWindowManager windowManager = getWindowManagerService();
                //通过Binder调用获取Session代理对象
                sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) {
                                ValueAnimator.setDurationScale(scale);
                            }
                        },
                        imm.getClient(), imm.getInputContext());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        return sWindowSession;
    }
}

WindowManagerService#openSession
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
        IInputContext inputContext) {
    ....
    //创建Session对象
    Session session = new Session(this, callback, client, inputContext);
    return session;
}
复制代码

在ViewRootImpl的构造方法中获取了WindowManagerServicesession代理对象。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
            ....
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                //创建InputChannel     
                mInputChannel = new InputChannel();
            }
            ....
            try {
                ....
                // 通过Binder在SystemServer进程中完成InputChannel的注册
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
            } 
            ....
            if (mInputChannel != null) {
                if (mInputQueueCallback != null) {
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
                //创建WindowInputEventReceiver对象
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                        Looper.myLooper());
            }
            ....
            //组装处理事件的责任链
           mSyntheticInputStage = new SyntheticInputStage();
           InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
           InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
        "aq:native-post-ime:" + counterSuffix);
           InputStage earlyPostImeStage = new     EarlyPostImeInputStage(nativePostImeStage);
           InputStage imeStage = new ImeInputStage(earlyPostImeStage,
        "aq:ime:" + counterSuffix);
           InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
           InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
        "aq:native-pre-ime:" + counterSuffix);
          ....
        }
    }
}
复制代码

setView方法的流程里创建了InputChannel对象然后将InputChannel对象注册给WMS,最后创建WindowInputEventReceiver来接收处理事件。Session的addToDisplay方法最终会调用到WindowManagerServiceaddWindow方法。

public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
        ....
        //创建WindowState对象
        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], seq, attrs, viewVisibility, session.mUid,
                session.mCanAddInternalSystemWindow);
        ....
        
        final boolean openInputChannels = (outInputChannel != null
                && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
        if  (openInputChannels) {
            //WindowState对象绑定InputChannel
            win.openInputChannel(outInputChannel);
        }
        ....
}

WindowState#openInputChannel
void openInputChannel(InputChannel outInputChannel) {
    ....
    //根据WindowState的HashCode以及Tag来生成InputChannel名称
    String name = getName();
    //创建一对InputChannel[见小节2.6]
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
    //第一个是服务端Channel
    mInputChannel = inputChannels[0];
    //第二个是客户端Channel
    mClientChannel = inputChannels[1];
    //将socket服务端Channel保存到WindowState的mInputChannel
    mInputWindowHandle.inputChannel = inputChannels[0];
    if (outInputChannel != null) {
        //socket客户端Channel传递给outInputChannel
        mClientChannel.transferTo(outInputChannel);
        mClientChannel.dispose();
        mClientChannel = null;
    } else {
      ....
    }
    //利用socket服务端Channel作为参数注册到IMS
    mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
复制代码

addWindow方法会创建WindowState,然后InputChannel会根据WindowState生成的name创建两个InputChannel对象,一个作为客户端、一个作为服务端。服务端保存到WindowState的mInputChannel并注册到IMS,客户端传递给outInputChannel,最终传递给ViewRootImpl的mInputChannel,触摸事件就可以通过mInputChannel来到ViewRootImplWindowInputEventReceiver中处理了。整体的流程可以归纳为两部分:
1)创建socket pair,作为InputChannel,一个作为服务端,一个作为客户端。
2) IMS.registerInputChannel()注册InputChannel,监听socket服务端。

2.3WindowInputEventReceiver

在前面ViewRootImpl的setView方法中创建了WindowInputEventReceiver对象,WindowInputEventReceiver用来接收事件和分发事件。

final class WindowInputEventReceiver extends InputEventReceiver {
    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
        //构造方法传入了客户端InputChannel
        super(inputChannel, looper);
    }
    @Override
    public void onInputEvent(InputEvent event, int displayId) {
        //InputEvent事件会回调到onInputEvent方法,此处将输入事件加入队列,最后会调用到ViewRootImpl的enqueueInputEvent方法
        enqueueInputEvent(event, this, 0, true);
    }
    @Override
    public void onBatchedInputEventPending() {
        if (mUnbufferedInputDispatch) {
            super.onBatchedInputEventPending();
        } else {
            scheduleConsumeBatchedInput();
        }
    }
    @Override
    public void dispose() {
        unscheduleConsumeBatchedInput();
        super.dispose();
    }
}

ViewRootImpl#enqueueInputEvent
void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    ....
    if (processImmediately) {
        //方法会调用到这里
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}
ViewRootImpl#doProcessInputEvents
void doProcessInputEvents() {
     ....
     //关键代码
     deliverInputEvent(q);
     ....
}
ViewRootImpl#deliverInputEvent
private void deliverInputEvent(QueuedInputEvent q) {
    ....
    InputStage stage; //处理输入事件
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }

    if (q.mEvent instanceof KeyEvent) {
        mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
    }

    if (stage != null) {
        handleWindowFocusChanged();
        stage.deliver(q);  //事件的责任链调用
    } else {
        finishInputEvent(q);
    }
}
复制代码

InputStage是处理输入的责任链,在调用deliver时会遍历责任链传递事件,事件分发完成后会调用finishInputEvent,告知SystemServer进程的InputDispatcher线程,最终将该事件移除,完成此次事件的分发消费。InputStage有多个处理类型的子类,这里主要看ViewPostImeInputStage类,这个是处理事件分发到View的相关逻辑。

  • SyntheticInputStage。综合处理事件阶段,比如处理导航面板、操作杆等事件。
  • ViewPostImeInputStage。视图输入处理阶段,比如按键、手指触摸等运动事件,我们熟知的view事件分发就发生在这个阶段。
  • NativePostImeInputStage。本地方法处理阶段,主要构建了可延迟的队列。
  • EarlyPostImeInputStage。输入法早期处理阶段。
  • ImeInputStage。输入法事件处理阶段,处理输入法字符。
  • ViewPreImeInputStage。视图预处理输入法事件阶段,调用视图viewdispatchKeyEventPreIme方法。
  • NativePreImeInputStage。本地方法预处理输入法事件阶段。
final class ViewPostImeInputStage extends InputStage {
    public ViewPostImeInputStage(InputStage next) {
        super(next);
    }
    @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) {
       final MotionEvent event = (MotionEvent)q.mEvent;
       ....
       //mView就是DecorView,流程就真正来到了View的dispatchPointerEvent
       boolean handled = mView.dispatchPointerEvent(event);
       maybeUpdatePointerIcon(event);
       maybeUpdateTooltip(event);
       mAttachInfo.mHandlingPointerEvent = false;
       if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
          mUnbufferedInputDispatch = true;
          if (mConsumeBatchedInputScheduled) {
             scheduleConsumeBatchedInputImmediately();
          }
     }
       return handled ? FINISH_HANDLED : FORWARD;
   }
}

View#dispatchPointerEvent
public final boolean dispatchPointerEvent(MotionEvent event) {
    if (event.isTouchEvent()) {
        //触摸事件分发,调用到DecorView的方法
        return dispatchTouchEvent(event);
    } else {
        return dispatchGenericMotionEvent(event);
    }
}
DecorView#dispatchTouchEvent
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    //activity就是DecorView的callBack
    final Window.Callback cb = mWindow.getCallback(); 
    return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
            ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
复制代码

至此为止,屏幕触摸事件就彻底来到了Activity的DecorView中,之后的流程就是常见的事件分发流程了。

3.总结

整个触摸事件的分发流程可以大体分为几个部分:
1)触摸手机屏幕使硬件层驱动接收到触摸信号,由Linux内核层发送触摸事件的Event
2)InputManagerService通过InputReader线程接收硬件层的Event,并采用 InputDispatcher线程进行分发(InputReaderInputDispatcher都是native线程)。
3)InputManagerService分发出的事件交给InputChannel进行分发,分发的过程涉及进程间的通信,采用的是Socket,这个过程中又经过了WindowManagerService
4)ViewRootImpl通过WindowInputEventReceiver接收InputChannel传过来的InputEvent
5)ViewRootImpl将接收的InputEvent发送给DecorView
6)DecorView将事件发送给ActivityActivity将事件发送给PhoneWindowPhoneWindow又将事件发送回DecorView
经过上述6个步骤事件就来到了我们App的界面层了,之后的事件分发就是我们常见的ViewGroup->View的流程了。

参考文章
juejin.cn/post/684490…
juejin.cn/post/696548…

Guess you like

Origin juejin.im/post/7105380689503059975