EDD

浅谈事件驱动编程

try talking about EDD

Posted by 宸笙 on July 8, 2018

导语

2014年还是大二的我看到一个前辈讲解事件驱动编程并把EventBusOtto作为例子演示,那时候限于开发经验,难理解它的必要性,慢慢才体会到它的必要和便利;

场景

当你做1-2个App以上的时候会发现,随着App和业务的逐渐复杂,经常遇到这种通知依赖的场景:ActivityA的不同状态ActivityB需要获取并做不同处理,包括Activity和Fragment间,尽管我们可以通过接口回调或者观察者模式来解决,如果包含层次嵌套那很难拿到需要的状态,代码无疑是冗余不优雅的;那么怎么解决呢?

  1. 绑定系统事件,如广播,通过发送系统广播,其他注册来对应广播的就能收到事件;But,广播是耗性能的,虽然有本地广播的存在,但还是不够优雅;
  2. 从借助广播能解决思考,把问题提高下层次,广播是全局的,借助系统机制便能解决,

    组件间的事件通知是局部性的,低层次的;

    所以,可以有一个全局性的事件管理模块

    之所以代码冗余是因为持有引用回调代码

    所以,可以加一个中间层来实现解耦和简化代码

  3. 思路:当低层次的局部性问题得不到较好解决时,可以上升到一定层次并较大程度解决,当模块复杂时候,也可上升到架构层次,高层抽象的解决方案往往具有较好的复用性,也为后续抽取成为基础SDK库做了准备;

什么是事件驱动编程

EventDriveDevelop;

全局–总线->EventBus

思考为何客户端才较多出现的事件驱动

移动互联网强调用户体验,用户交互事件也繁多复杂,在结合App业务,事件就比较复杂且频率高了,这点在Web前端和移动端都是同理的;

我们需要怎样的事件处理模块?

  1. 较高的抽象,不依赖于Android组件
  2. 轻量
  3. 性能好(第一次实现可以先忽略)
  4. 好用和易用

    宏观目标:好用,步骤少,学习成本低

    具体实现:心智负担少(不用懂得复杂的模型),封装,Api明确简洁符合直觉,暴露较少概念;

  5. 较低的侵入性

    定位是易替换的SDK,易插拔;

    一般建议开发者添加个中间层(管理类)去封装第三方SDK,进一步降低替换成本;

尝试实现

结构

  1. register
    • ModuleA -> EventBus
    • ModuleB -> EventBus
  2. post
    • EventBubs ->(notify) ModuleA
    • EventBubs ->(notify) ModuleB

从实现反推设计(自底向上)

  1. 我们需要怎样的使用体验?
/**
 * 封装EventBus并对外暴露接口
 */
class EventBusProvider{
    
    public static void register(Class<?> subscriberType){
        EventBus.register(subscriberType);
    }
    
    public static void unRegister(Class<?> subscriberType){
        EventBus.unRegister(subscriberType);
    }
    
    public static void post(Object data){
        EventBus.post(data);
    }
}

class ActivityA{
    @Override
    public void onCreate(){
        // 注册到事件总线
        EventBusProvider.register(this);
    }
    
    // 先默认采用惯例onEvent方法名,不用自定义注解
    public void onEvent(UserLogin data){
       // 此时收到回调过来的事件数据 
    }
}

/**
 * 定义事件发生时传递的数据
 */
class UserLogin{
    String userId;
    String userName;
    // constructor
}

class ActivityB{
    
    private void doLogin(){
        // mock 
        UserLogin data = new UserLogin("001","宸笙");
        // 触发事件
        EventBusProvider.post(data);
    }
}
  1. 实现思路

    注册

    • 把订阅者如Activity的Class对象和对应的事件保存;
    • Map<Object,List>

    解注册

    • 和注册相反的操作,对相关集合remove操作即可;

    post事件

    • 实现重点:根据事件的类型找到订阅该事件的订阅组件并一一回调方法;
  2. 具体实现

    V1;

    Code;

  3. 使用

  1. 小结
  2. 延伸
  3. 优化点