一句话说透Android里面事件分发的本质

139 阅读3分钟

一句话总结:
事件分发的本质是责任链模式的典型应用——就像医院的分诊系统:

  • **患者(触摸事件)**按流程经过多个科室(View层级)
  • **分诊台(ViewGroup)**决定是自己治疗还是转给下级
  • **最终科室(View)**进行实际治疗(处理事件)

一、核心设计原理

1. 责任链模式(看病流程)

患者挂号 → 分诊台 → 专科医生 → 检查治疗  
   ↓           ↓          ↓  
Activity → ViewGroup → View  

设计目的

  • 解耦处理逻辑(各科室独立工作)
  • 动态决定处理者(分诊台灵活调度)

2. 事件消费机制(治标还是治本)

// return true:表示治好患者(事件不再传递)  
override fun onTouchEvent(e: MotionEvent): Boolean {  
    return true   
}  
 
// return false:表示治不了(事件继续传递)  
override fun onTouchEvent(e: MotionEvent): Boolean {  
    return false  
}  

二、底层运作流程

阶段1:事件采集(患者挂号)

系统底层通过InputManagerService

  1. 监听屏幕触摸中断
  2. 通过EventHub读取/dev/input事件
  3. 封装为MotionEvent对象

阶段2:事件传递(科室间转运)

Activity.dispatchTouchEvent()    
       ↓  
PhoneWindow.superDispatchTouchEvent()   
       ↓  
DecorView.dispatchTouchEvent()   
       ↓  
ViewGroup.dispatchTouchEvent()   
       ↓  
View.dispatchTouchEvent()   

关键点

  • 每个环节都可中断传递(类似科室拒诊)
  • 完整事件序列(DOWN→MOVE→UP)必须由同一View处理

阶段3:事件处理(实际治疗)

public boolean onTouchEvent(MotionEvent event) {  
    // 这里实现具体业务逻辑  
    return super.onTouchEvent(event);   
}  

处理优先级

触摸监听器 > View自身处理 > 点击监听器  

三、为什么这样设计?

1. 历史兼容需求

早期Android实体键为主,触控普及后需要兼容旧设计:

  • Back键 → Activity处理
  • Menu键 → Window处理
  • 触控操作 → View处理

2. UI层级结构特性

View树形结构天然适合逐级传递:

Activity(根)  
└─ FrameLayout(Window)  
   └─ LinearLayout(DecorView)  
      └─ Button(子View

3. 性能优化考虑

  • 避免全局广播(精准定位处理者)
  • 减少无效遍历(通过拦截机制提前终止)

四、核心冲突解决方案

1. 滑动冲突(科室抢病人)

案例:ViewPager嵌套RecyclerView

// 解决思路:根据滑动方向判断责任方  
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {  
    when(计算滑动角度) {  
        水平滑动 → 自己处理(ViewPager翻页)  
        垂直滑动 → 不拦截(交给RecyclerView滚动)  
    }  
}  

2. 多点触控(多个患者同时就诊)

系统通过pointerId区分不同手指:

event.getPointerId(actionIndex)  // 获取当前手指ID  
event.findPointerIndex(pointerId)  // 查找对应手指数据  

五、本质特性总结表

特性原理类比
传递顺序树形结构深度优先遍历医院科室层级
消费机制布尔值控制事件流病历本签章
拦截能力ViewGroup专属特权分诊台调度权
事件序列必须完整处理同一序列患者完整疗程

六、避坑指南

坑1:事件丢失

症状:快速滑动时事件不连贯
解决方案

override fun onTouchEvent(e: MotionEvent): Boolean {  
    when(e.actionMasked)  {  
        ACTION_CANCEL → 处理异常中断  
        ACTION_POINTER_UP → 处理多指抬起  
    }  
    return true  
}  

坑2:内存泄漏

错误示范

// 在自定义View中直接持有Activity引用  
class MyView(context: Context) : View(context) {  
    private val activity = context as Activity // 危险!  
}  

正确姿势

// 使用弱引用  
private var weakActivity = WeakReference(context)  

坑3:性能瓶颈

优化技巧

// 减少onDraw调用频率  
override fun onTouchEvent(e: MotionEvent): Boolean {  
    when(e.action)  {  
        ACTION_MOVE → {  
            updateData()  
            postInvalidate() // 异步刷新  
        }  
    }  
    return true  
}  

终极口诀:
事件分发责任链,层级传递像医院
ViewGroup是分诊台,View是医生把病看
拦截消费靠返回,触摸序列要完整
冲突解决方向判,内存泄漏需防范!