Android事件分发源码解析
用快递系统类比源码流程:
硬件驱动(快递员收件) → Kernel(分拣中心) → SystemServer(总仓) → App进程(派送站) → Activity(站长) → View树(快递柜)
一、事件产生与传递(快递收件流程)
1. 底层事件采集
// Native层核心代码(frameworks/native/libs/input/InputTransport.cpp )
status_t InputPublisher::publishMotionEvent(...) {
// 将硬件事件封装为MotionEvent
parcel->writeInt32(deviceId);
parcel->writeInt32(source);
parcel->writeFloat(x);
// ...
}
关键对象:
InputReaderThread:监听/dev/input事件InputDispatcher:将事件分发到对应窗口
2. 跨进程传递
// SystemServer进程(frameworks/base/services/core/java/com/android/server/input/InputManagerService.java )
class InputManagerService {
void injectInputEvent(InputEvent event, int mode) {
// 通过Socket通信将事件发送到App进程
mWindowManagerService.injectInputEvent(event);
}
}
通信载体:
InputChannel:基于共享内存和Socket的IPC通道
二、应用层事件处理(快递派送流程)
1. 入口:Activity分发
// Activity.java (API 34+)
public boolean dispatchTouchEvent(MotionEvent ev) {
if (getWindow().superDispatchTouchEvent(ev)) {
return true; // 窗口处理成功
}
return onTouchEvent(ev); // 自己处理
}
关键调用链:
Activity.dispatchTouchEvent()
↓
PhoneWindow.superDispatchTouchEvent()
↓
DecorView.dispatchTouchEvent()
2. 核心:ViewGroup拦截机制
// ViewGroup.java (关键源码精简)
public boolean dispatchTouchEvent(MotionEvent ev) {
// 步骤1:判断是否拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
intercepted = onInterceptTouchEvent(ev);
} else {
intercepted = true; // 后续事件默认拦截
}
// 步骤2:不拦截则分发给子View
if (!canceled && !intercepted) {
for (View child : children) {
if (child.dispatchTouchEvent(ev)) {
mFirstTouchTarget = child; // 记录首个处理者
return true;
}
}
}
// 步骤3:自己处理
return super.dispatchTouchEvent(ev);
}
设计亮点:
mFirstTouchTarget缓存首个处理View,确保事件序列一致性- ACTION_DOWN时重置状态,避免内存泄漏
3. 终点:View处理逻辑
// View.java
public boolean onTouchEvent(MotionEvent event) {
// 优先级:TouchListener > onTouchEvent > ClickListener
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED) {
if (mOnTouchListener.onTouch(this, event)) {
return true;
}
}
return performClickInternal(event); // 触发点击事件
}
性能优化点:
- 快速点击过滤(ViewConfiguration.getTapTimeout() = 100ms)
三、高级机制源码揭秘
1. 事件冲突解决原理
// ViewParent接口
public interface ViewParent {
void requestDisallowInterceptTouchEvent(boolean disallowIntercept);
}
// ViewGroup中的实现
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept != mGroupFlags) {
mGroupFlags = disallowIntercept; // 修改拦截标志位
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
生效条件:仅在ACTION_DOWN之后调用有效
2. 多点触控实现
// Native层事件处理(frameworks/native/libs/input/InputTransport.cpp )
void InputConsumer::consumeBatch(...) {
for (size_t i=0; i<count; i++) {
event->initialize(...);
event->setActionPointerIndex(pointerIndex); // 区分不同手指
}
}
应用层获取方式:
event.getPointerId(actionIndex) // 每个手指唯一ID
四、源码设计亮点总结
| 设计思想 | 源码体现 | 解决的问题 |
|---|---|---|
| 责任链模式 | ViewGroup事件分发逻辑 | 动态决定事件处理者 |
| 观察者模式 | OnTouchListener回调 | 灵活扩展事件处理 |
| 状态缓存 | mFirstTouchTarget | 保证事件序列连续性 |
| 性能优化 | 历史事件批量处理 | 减少UI线程负载 |
五、调试技巧
1. 事件轨迹可视化
// 在开发者选项中开启:
Settings → System → Developer options → Input → Touch tracking
效果:实时显示触摸点轨迹和事件流
2. 源码断点秘籍
关键断点位置:
1. ViewRootImpl#processPointerEvent (入口)
2. DecorView#dispatchTouchEvent
3. ViewGroup#dispatchTouchEvent (行号:2467)
4. View#onTouchEvent (行号:15123)
3. ADB事件注入
# 模拟滑动操作
adb shell input swipe --curve 100 500 300 800 1000
终极源码口诀:
事件分发起于Input,跨进程传递靠Channel
Activity是入口站长,ViewGroup当分拣员
拦截方法onIntercept,消费标志看返回值
多点触控存Pointer,冲突解决改Flag!