Android点击事件处理过程整理

在Android系统中,点击事件的处理过程可以分为以下几个主要阶段,从底层的 InputReader 读取输入事件,到 InputDispatcher 分发事件,再到应用进程的 View 进行事件分发。以下是详细的流程:


1. 事件读取 (InputReader)

类名:InputReader
方法:loopOnce()

  • InputReader 运行在 EventHub 线程中,它会从 /dev/input 读取触摸屏事件(如 EV_ABS, EV_KEY)。
  • InputReader::processEventsLocked() 解析触摸事件,构建 MotionEvent,并传递给 InputDispatcher

2. 事件分发 (InputDispatcher)

类名:InputDispatcher
方法:dispatchOnce()

  • InputDispatcher 运行在 InputDispatcherThread 线程中,它从 InputReader 获取 MotionEvent 并进行分发。
  • InputDispatcher::enqueueInboundEventLocked() 将事件放入队列。
  • InputDispatcher::dispatchOnceInnerLocked() 负责遍历 窗口管理器 (WMS) 的窗口栈,并找到对应的窗口 (Window)
  • InputDispatcher::dispatchMotionLocked() 通过 Binder 机制调用 InputChannel.sendMessage() 将事件发送给目标应用进程。

3. 跨进程通信 (应用进程接收事件)

类名:InputChannelInputEventReceiver
方法:dispatchInputEvent()

  • InputChannel 是 Android 跨进程通信的通道,它在 InputDispatcher应用进程的 UI 线程 之间传输 MotionEvent
  • InputEventReceiver::handleEvent() 监听 InputChannel 的事件,并通过 NativeInputEventReceiver 传递到 ViewRootImpl
    注意,这里跨进程采用的是socketpair:
public final class InputMethodManagerService extends IInputMethodManager.Stub
        implements Handler.Callback {
    void requestClientSessionLocked(ClientState cs) {
        if (!cs.sessionRequested) {
            if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
            final InputChannel serverChannel;
            final InputChannel clientChannel;
            {
                final InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
                serverChannel = channels[0];
                clientChannel = channels[1];
            }

            cs.sessionRequested = true;
  }
}
public final class InputChannel implements Parcelable {
public static InputChannel[] openInputChannelPair(String name) {
        InputChannel channels[] = new InputChannel[2];
        long[] nativeChannels = nativeOpenInputChannelPair(name);
        for (int i = 0; i< 2; i++) {
            channels[i] = new InputChannel();
            channels[i].setNativeInputChannel(nativeChannels[i]);
        }
        return channels;
    }
}
static jlongArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
      std::unique_ptr<InputChannel> serverChannel;
    std::unique_ptr<InputChannel> clientChannel;
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
}
status_t InputChannel::openInputChannelPair(const std::string& name,
                                            std::unique_ptr<InputChannel>& outServerChannel,
                                            std::unique_ptr<InputChannel>& outClientChannel) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%s(%d)", name.c_str(),
              strerror(errno), errno);
        outServerChannel.reset();
        outClientChannel.reset();
        return result;
    }
...
}

4. 事件进入 View 层 (ViewRootImpl)

类名:ViewRootImpl
方法:dispatchInputEvent()

  • ViewRootImpl 是 View 层的入口点,它会调用 ViewRootImpl::deliverInputEvent()
  • 事件最终由 ViewRootImpl::XXXInputStage::processPointerEvent() 处理,并传递给mView,即 DecorView。最终会调用DecorView::dispatchTouchEvent
  • XXXInputStage根据类型不一样类名不一样
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }
...
handled = handled || mView.dispatchPointerEvent(event);
...
class View {
    public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }
}

5. 事件传递到 DecorView

类名:DecorView
方法:dispatchTouchEvent()

  • DecorView 继承自 FrameLayout,是整个窗口的根 View。
  • 事件先经过 DecorView::dispatchTouchEvent(),这个函数会调用mWindow(PhoneWindow)的callback回调, 这个callback回调就是Activity
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final Window.Callback cb = mWindow.getCallback();
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }

6. 事件传递到 Activity

类名:Activity
方法:dispatchTouchEvent()

  • Activity 默认会将事件传递给 Window,然后交给 ViewGroup 处理:
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
    
  • 其中 getWindow().superDispatchTouchEvent(ev) 会调用 DecorView::superDispatchTouchEvent(ev)
    public boolean superDispatchTouchEvent(MotionEvent event) {
      // 这里就是调用Viewgroup的dispatchTouchEvent
        return super.dispatchTouchEvent(event);
    }

7. 事件传递到 ViewGroup

类名:ViewGroup
方法:dispatchTouchEvent()

  • ViewGroup::dispatchTouchEvent() 负责将事件分发给子 View
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (onInterceptTouchEvent(ev)) {
            return super.dispatchTouchEvent(ev);
        }
        return child.dispatchTouchEvent(ev);
    }
    
  • onInterceptTouchEvent(ev) 用于拦截事件。

8. 事件传递到最终的 View

类名:View
方法:dispatchTouchEvent()

  • View::dispatchTouchEvent() 先调用 onTouchListener 监听器,如果没有被消费,则调用 onTouchEvent()
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (onTouchListener != null && onTouchListener.onTouch(this, event)) {
            return true;
        }
        return onTouchEvent(event);
    }
    
  • onTouchEvent() 处理点击、滑动等事件,默认返回 false,表示未消费事件,会向上传递。

9. 事件回溯 (事件未被消费)

  • 如果 View 没有消费事件,ViewGroup 会继续查找其他 View 处理。
  • 如果没有 View 处理事件,ViewGroup::onTouchEvent() 可能会处理,例如 ScrollView 可能会消费滑动事件。
  • 如果 ViewGroup 也没有消费事件,最终回到 Activity::onTouchEvent(),然后可能触发 onBackPressed()finish()

总结

  1. InputReader 读取触摸事件。
  2. InputDispatcher 处理并分发事件。
  3. InputChannel 通过 Socket 机制发送事件到应用进程。
  4. ViewRootImpl 作为入口,处理事件并传递到 DecorView
  5. DecorView -> Activity -> Window -> ViewGroup -> View,层层分发。
  6. View 通过 dispatchTouchEvent() -> onTouchListener -> onTouchEvent() 处理点击事件。

这个过程涵盖了 Android 触摸事件从 内核应用层 的完整传递路径。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容