安卓开发:应用间通讯模式

702 查看

安卓应用主要由Activities、Services、Fragments和辅助类等组成。然而,在组件之间进行通讯却需要一些技巧。重用代码、解耦和即插即用对应用设计提出了更高的要求。通常,使用消息总线(MessageBus)会是一种很好的选择。

本文介绍了安卓开发中消息总线概念、相关工具和库函数,以及设计中需要考虑的注意事项。 

 安卓应用主要由Activities、Services、Fragments和辅助类等组成。然而,在组件之间进行通讯却需要一些技巧。想要写出松散耦合、能即插即用的可重用代码,也需要技巧。本文的目标是避免紧耦合。

紧耦合—组件彼此直接引用,直接互相调用, 在下面的代码中, 我们在 MenuFragment 中保留了一个对 MagazineActivity 的引用, 所以MenuFragment 就和 MagazineActivity 耦合在一起, 没有MagazineActivity这个类, MenuFragment就无法运行。

这就导致了组件间大量直接的依赖, 这样的设计中, 一个类的变化导致大量相关的类的变化, 维护起来相当棘手。 所以,如果是由多个开发人员共同开发,这个程序将会变的非常容易出错。

所以,松耦合是以减少组件之间的依赖为目标,一个松耦合系统可以很轻松地把系统解构为多个不同的元素, 使得整个系统可以更有弹性和可维护。对于多个开发人员共同开发的场景, 每个开发人员只要开发相对独立的模块就可以很轻松的和其他的模块通过标准协议相互联系。

传统解耦方案:接口

接口对解耦很有帮助, 类之间通过接口相互通信而不直接通信,一个类可以提供一个接口给其他的类通信, 这样就只是提供了一个抽象层,而不用操心具体的实现, 这就像ATM机的人机交互一样, 只要你会用键盘, 你就能在任何ATM机上进行交易,而不用管各个不同ATM机的内部实现细节。

下面是使用接口的一些缺点:

  1.  组件直接需要彼此了解才能传递接口,所以仍然会有一点依赖,
  2.  接口之间不能通过意图传递 ,所以有时候android 的特定的组件之间不能相互通信
  3.  在一个有很多通信的应用中, 接口将会变得很臃肿, 导致你要写大量的废代码,徒增复杂度, 多个组件相互沟通的时候使用接口要链式传递消息,例如,在一个Activity你的一个辅助类需要给一个Fragment传递信息时,这将导致接口链

优雅的解决方案: 消息通道

通信是基于订阅发布的机制, 一个发布者广播一个事件, 订阅者可以接到通知,并触发动作。这样可以做到纯松耦合。

类似于ios中的 NSNotification 和 NSNotificationCenter 。

MessageBus的实现

1.基于implicit-Intent (隐藏意图) 和BroadcastReceiver(广播器)

基于implicit-Intent和BroadcastReceiver(广播器)的通信是一种消息通信的实现. 这个intent使用sendBroadcast() startActivity() 方法匹配action 并且 使用相应的IntentFilter过滤BroadcastReceivers 或者Activities, 它们会得到通知。有以下优点和缺点:

优点:可以联系不同的应用, 例如, 你的应用发布一个展示图片的implicit-Intent, 并且可以过滤选择可以展示图片的Activities。

缺点: Intent中的数据存放在bundle中,而bundles 不能存放复杂的数据结构, 自己实现Serializable或Parcelable接口开销太大。

FluffyEvents 是一个基于implicit-Intent和BroadcastReceiver(广播器)实现的消息通信库

 2.EventBus: 基于MessageBus的事件

不同的事件可以被发布和订阅, 无论何时当一个消息发布者把消息放到bus中, 一个订阅者就会得到通知, 一个事件可以是任何java 类,  如下:

EventBus: 幕后工作:

EventBus使用map 数据类型跟踪事件和活动类的订阅者方法,Otto中这样使用:

这个maping 只要下面两步就搞定了

1.  使用反射,循环遍历类中的方法
2.  如果一个基于注释EventBus,遍历注释来填充map , 在基于命名约定的EventBus中,使用命名约定来填充映射

注释: 注解处理在java中是一项高度密集的任务, 所以基于注解的EventBus会产生些许延迟

这个map在每次Bus上注册类和注销类的时候更新,  通常一个Activity 或者 Fragment 在onRuesume方法中注册, 在onPause方法中注销。辅助类需要在它们的主类(owning class),如Activity, Fragment或Service中注册或者注销。当一个事件发布到一个Bus上面,上述map就会被遍历, 映射在Map上面的方法会被调用。

由于事件这个概念在Android中不是标准, 所以EnventBus 不能用于 app之间的通信, 而且,Bus 类作用于一个应用之内。 目前Android没有内嵌的Bus实现, 当然你可以自己实现一个,但是使用已经存在的类库是更好的方式, 例如下面的两个类库:

Square’s Otto:  这是一个注解方式实现的通信库, 简单易用, 基于Guava库中的EventBus的实现, Otto是针对Android的优化,  提供线程选项但是很有局限。

GreenRobot’s EventBus:  这是基于命名约定的EventBus, 但是性能上来看,优于其他类库,有很多线程选项

结论:

权衡利弊总是少不了的, 在本文的讨论中, 平衡点在于接口与EventBus的使用之间(在一个程序中两者都会涉及), 接口导致废代码,但目前是标准而高效的做法, EventBus使得代码简洁, 但是反射和注解使得性能受损,   如果你不需要连接多个类, 你应该使用接口,EventBus应该用于多个不直接相连类之间的通信。