我所理解的Android组件化之通信机制 (2)

事件总线

事件总线通过记录对象,使用监听者模式来通知对象各种事件,比如在现实生活中,我们要去找房子,一般都去看小区的公告栏,因为那边会经常发布一些出租信息,我们去查看的过程中就形成了订阅的关系,只不过这种是被动去订阅,因为只有自己需要找房子了才去看,平时一般不会去看。小区中的公告栏可以想象成一个事件总线发布点,监听者则是哪些想要找房子的人,当有房东在公告栏上贴上出租房信息时,如果公告栏有订阅信息功能,比如引入门卫保安,已经把之前来这个公告栏要查看的找房子人一一进行电话登记,那么一旦有新出租消息产生,则门卫会把这条消息一一进行短信群发,那么找房子人则会收到这条消息进行后续的操作,是马上过来看,还是延迟过来,则根据自己的实际情况进行处理。在目前开源库中,有EventBus、RxBus就是采用这种发布/订阅模式,优点是简化了Android组件之间的通信方式,实现解耦,让业务代码更加简洁,可以动态设置事件处理线程和优先级,缺点则是每个事件需要维护一个事件类,造成事件类太多,无形中加大了维护成本。那么在组件化开源框架中有ModuleBus、CC 等等。

这两者模式更详细的对比,可以查看这篇文章多个维度对比一些有代表性的开源android组件化开发方案

实现方案

事件总线,又可以叫做组件总线,路由+接口,则相对好理解点,今天从阅读它们框架源码,我们来对比这两种实现方案的不同之处。

组件总线

这边选取的是ModuleBus框架,这个方案特别之处在于其借鉴了EventBus的思想,组件的注册/注销和组件调用的事件发送都跟EventBus类似,能够传递一些基础类型的数据,而并不需要在Base Moudel中添加额外的类。所以不会影响Base模块的架构,但是无法动态移除信息接收端的代码,而自定义的事件信息类型还是需要添加到Base Module中才能让其他功能模块索引。

其中的核心代码是在与 ModuleBus 类,其内部维护了两个ArrayMap键对值列表,如下:

/** * Object methodClass * String methodName; * MethodInfo method info */ private static ArrayMap<Object,ArrayMap<String,MethodInfo>> moduleEventMethods = new ArrayMap<>(); /** * Class IBaseClient.class * String methodName * Object methodClass */ private static ArrayMap<Class<?>,ArrayMap<String,ArrayList<Object>>> moduleMethodClient = new ArrayMap<>();

在使用方法上,在onCreate()和onDestroy()中需要注册和解绑,比如

ModuleBus.getInstance().register(this); ModuleBus.getInstance().unregister(this);

最终使用类似EventBus 中 post 方法一样,进行两个组件间的通信。这个框架的封装的post 方法如下

public void post(Class<?> clientClass,String methodName,Object...args){ if(clientClass == null || methodName == null ||methodName.length() == 0) return; ArrayList<Object> clientList = getClient(clientClass,methodName); if(clientList == null) return; try{ for(Object c: clientList){ try{ ArrayMap<String,MethodInfo> methods = moduleEventMethods.get(c); Method method = methods.get(methodName).m; if(method == null){ Log.e(TAG,"cannot find client method"+methodName +"for args["+args.length+"]" + Arrays.toString(args)); return; }else if(method.getParameterTypes() == null){ Log.e(TAG,"cannot find client method param:"+method.getParameterTypes() +"for args["+args.length+"]" + Arrays.toString(args)); return; }else if(method.getParameterTypes().length != args.length){ Log.e(TAG,"method "+methodName +" param number not matched:method("+method.getParameterTypes().length+"), args(" + args.length+")"); return; } method.invoke(c,args); }catch (Throwable e){ Log.e(TAG,"Notifiy client method invoke error.",e); } } }catch (Throwable e){ Log.e(TAG,"Notify client error",e); } }

可以看到,它是通过遍历之前内部的ArrayMap,把注册在里面的方法找出,根据传入的参数进行匹配,使用反射调用。

接口+路由

接口+路由实现方式则相对容易理解点,我之前实践的一个项目就是通过这种方式实现的。具体地址如下:DemoComponent 实现思路是专门抽取一个LibModule作为路由服务,每个组件声明自己提供的服务 Service API,这些 Service 都是一些接口,组件负责将这些 Service 实现并注册到一个统一的路由 Router 中去,如果要使用某个组件的功能,只需要向Router 请求这个 Service 的实现,具体的实现细节我们全然不关心,只要能返回我们需要的结果就可以了。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdsjs.html