一个项目的开启时,需要深入评估工具在编程中的代价和实现成效。工具直接影响着工作效率,首先要知道有什么工具(工具类及库)?怎么选择这些工具?怎么使用这些工具?使用这些工具有什么好处?怎么做才能做到更加高效,以减少工具消耗和人力消耗的计量?
时间维度:沟通时间,决策时间,编码时间,维护时间;
空间维度:产生工程包大小,运行内存,方法量。
一.本地广播:
LocalBroadcastManager是Android support包提供的一个工具包,用来在同一个应用内的不同组件间Broadcast进行通信,好处:
- 发送广播只会在自己的app内传播,不会泄露给其他的app,确保隐私信息回不泄露;
- 其他app无法向自己的app发送广播,不用被其他app干扰;
- 比全局广播更加高效。
//注册广播
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: 2019-07-10 接收广播的信息
String action = intent.getAction();
}
};
localBroadcastManager.registerReceiver(receiver,new IntentFilter("LOCAL_ACTION"));
//注销广播
localBroadcastManager.unregisterReceiver(receiver);
//发送广播消息
localBroadcastManager.sendBroadcast(new Intent("LOCAL_ACTION"));
二.组件之间通信机制:
- 事件总线EventBus:
Android中activity/fragment/service信息传递相对复杂,一开始考虑用广播的方式进行传递,但是系统的广播传递是耗时的而且非常容易被捕获。事件总线机制通过记录对象/使用监听者模式来通知对象各种事件。不需要原生过重的事件Broadcast机制的管理,并可以将信息传递给原生以外的各种高对象。
EventBus:是一款针对Android优化的发布/订阅事件总线,主要功能是代替Intent/Handler/Broadcast,在Fragment/Activity/Service/线程之间传递消息。优点是开销小,代码更优雅,以及将发送者和接收者解耦。EventBus框架中涉及四个部分——订阅者/发布者/订阅事件和事件总线。订阅者可以订阅多个事件,发送者可以发布任何事件,发布者同时也可以是订阅者。
//定义事件
public class MessageEvent{
//Additional fields if needed
}
//注册订阅者
EventBus.getDefault().register(this);
//订阅事件
@Subscribe
public void onEvent(AnyEventType eventType) {
//Do something
}
//发布事件
EventBus.getDefault().post(object);
//注销订阅
EventBus.getDefault().unregister(this);
在使用EventBus时一定要注意,订阅事件的对象在依附的activity/fragment/service一定要取消订阅,因为register是强引用,如不取消订阅,那么强引用会让对象无法得到内存回收,导致内存泄露。
- EventBus3.0:
EventBus3.0使用的方式和EventBus2.x是一致的。没开启索引系统的3.0版其反射效率比EventBus2.x版要低1-3倍,而开启索引系统,3.0版的速度要比2.x版快很多。
EventBus2.0使用的是运行时注解,运行时注解很大基础上是依赖于Java的反射规则,Java的反射是耗费效率的,采用反射的方式对整个注册的类所有方法进行扫描来进行注册。所以在一些低端Android手机中频繁使用反射,会对性能产生一定影响。
EventBus3.0使用的是编译时注解,Java文件在编译的时候,会将其编译为.class文件,再对class文件进行打包等一系列处理。而编译时注解在Java编译生成.class文件时就进行操作。可以想象是在代码编写完后,就创建出对文件或类的索引关系,将索引关系编入到apk中。具体是提供了EventBusAnnotaionProcessor注解处理器,在编译期通过读取@Subscribe()注解并解析/处理其中所包含的信息,然后生成Java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获取这些订阅者信息的速度更快。
RxBus一开始并不是一个库,它是基于RxJava响应式编程衍而来的,它是一种要引入RxJava和RxAndroid就可以简单地编写一个RxBus的事件总线。
/**
* @author wangyongyao
* @package com.example.demo1.demo
* @date 2019-08-11 10:48
* @decribe TODO
* @project
*/
public class RxBus {
private final Subject bus;
private RxBus() {
bus = new SerializedSubject<>(PublishSubject.create());
}
//创建单例模式
public static RxBus getInstance() {
return RxBusHolder.mInstatnce;
}
static class RxBusHolder{
private static RxBus mInstatnce = new RxBus();
}
//发送消息
public void post(Object object){
bus.onNext(object);
}
//接收消息
public <T> Observable <T> toObservable(Class<T> eventType) {
return bus.ofType(eventType);
}
}
//发送消息
RxBus.getInstance().post(new XXXEvent(long id));
//接收消息
Subscription rxScp = RxBus.getInstance().toObservable(XXXEvent.class)
.subcrible(new
Action1<XXXEvent>() {
public void call(XXXEvent event){
}
});
这里使用了静态内部类的单例,由于内部静态类只会被加载一次,所以实现方式线程安全的,比double check +volatile方式更加优雅。rxScp是Subscription的对象,方便生命周期结束时取消订阅事件。
- 组件化事件总线的考量:
传递消息时,三种工具需要先设定信息装载的容器,将XXXEvent的类作为信息装载的容器。这些信息容器的模版需要放在一个公共位置才能告诉其他功能模块,不同的信息的类型对应哪些信息。
通信事件都需要放到公共Base模块中,Base模块也需要依赖于事件总线框架。信息组件都需要放在Base模块中,其通信需要依赖于Base module,这样设计是不合理的。组件化要求功能模块独立,从设计的角度考虑,应该尽量少影响App module和Base module。
Base module需要做到尽量通用,不受其他功能模块的影响。而这个事件总线放在Base module中,每个模块增删时都需要添加或者删除事件信息模型到Base module中,而增删事件代码会让其它模块索引到这个事件的代码,造成错误,也需要删除,这样会破坏组件化的设计原则。虽然删除模块时并不一定需要删除Base module中的信息事件模型,但这会让事件总线的整个架构更加臃肿。
这就是目前组件化通信会遇到的瓶颈问题,如动态地将信息模块添加到公共的地方,然后被其他模块索引到,这是非常值得深究的问题,你也无法用编译时注解完成这一步骤,因为无法完成对编译前提供事件类的索引。
现阶段比较合适的组件化通信方式:
- ModuleBus:能传递一些基础类型的数据,而并不需要在Base module中添加额外的类,所以不会影响Base模块的框架,但是也无法动态移除信息接收端的代码。而自定义的事件信息模型还是需要添加到Base module中才能让其它模块索引。
- 组件化架构的Modularizationarchitecture库:每个功能模块中需要使用注解建立action事件,每个action完成一个事件动作。invoke只是方法名为反射,并为用到反射,而时使用接口方式调用,参数时通过HashMap<String,String>传递的,无法传递对象。
文章:Android架构思考(模块化、多进程)