一句话说透Android里面事件分发机制源码

211 阅读3分钟

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. 事件轨迹可视化

// 在开发者选项中开启:  
SettingsSystemDeveloper optionsInputTouch 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!