使用Roboguice依赖注入规划Android项目

关于依赖注入

Dependency Injection( 依赖注入)可以很好的帮助我们分离模块,降低耦合、提高可测试性。(PS:Roboguice 只是一个工具,依赖注入更多的是一种思想)

通常博主开发项目时喜欢以Activity 、Service 等组件作为顶级层入口,辅以各类接口作为业务服务。Activity 主要负责维护界面相关的东西,及提供功能所需要的上下文环境,引入功能实现需要的接口。

这些接口的实例通过Roboguice进行注入。(当然你也可以完全不使用Roboguice,但还是建议保留接口注入的设计)。

关于Roboguice

Roboguice 是基于guice-noaop 的android注入框架,

项目地址:https://github.com/roboguice/roboguice.利用Roboguice可以较轻松的注入各种服务,它认提供了各种android相关的注入如: injectView,injectResource 等。

遗憾的是这里将不对Roboguice的使用详细讲解。想了解 Roboguice 的读者可以查看官网的Wiki 或参考:http://www.imobilebbs.com/wordpress/archives/2480

需要注意的是Roboguice 分为 1.1 版和2.0及以上版本,这两个版本并不兼容,一般使用2.0即可,更简单方便。
下载需要的包

项目创建

创建android项目命名为:RoboguicePractice ,并添加Roboguice 相关包。

基本功能

项目仅包含一个Activity,界面上包含一个TextView和Button.点击Button 可查看当前时间。

为了使Demo更具代表性, Activity 需要引用 ITimeService 的接口来获取时间。ITimeService 接口的具体实现类AndroidTimeReand则依赖于ITimeRepository(数据源),这样就从逻辑上划分出一个基本的三层。

通常我喜欢把数据相关的模块(db、sharepreferene、net、cache等)归类到Repository中,对上层而言就形成一个数据来源接口。

注意:没有哪一种设计是万能,需要根据最实际的情况,不断的进行权衡,最终选择较合适的系统设计,并且要做好睡着系统的成长需要变更设计的准备

例如有的android程序比较简单,就完全不需要 IService 服务层。

项目包结构

这里创建一个viewmodel 用于辅助界面展示

使用静态类的实现方式

在正式开始项目前让我们看看一种常见的实现——通过静态的方式为 Activity提供服务。

1publicclassAndroidTimeRead{

2
3 public staticTimeviewmodelshowTime(){
4Timeviewmodelmodel= newTimeviewmodel();
5model.setTime(String.valueOf(System.currentTimeMillis()));
6 returnmodel;
7}
8
9}
10
11 public classMainActivity extendsActivity{
12
13 privateTextViewtxtShowTime;
14 privateButtonbtnShow;
15
16@Override
17 protected voidonCreate(BundlesavedInstanceState){
18 super.onCreate(savedInstanceState);
19setContentView(R.layout.activity_main);
20
21txtShowTime=(TextView)findViewById(R.id.txtShowTime);
22btnShow=(Button)findViewById(R.id.btnShow);
23btnShow.setonClickListener( newView.OnClickListener(){
24
25@Override
26 public voidonClick(Viewv){
27Timeviewmodelviewmodel=AndroidTimeRead.showTime();
28txtShowTime.setText(viewmodel.getTime());
29}
30});
31
32}
33
34}
代码很简单,也实现了我们的基本需要(如果产品到此为止的话)。但有两个明显的缺点,如果项目中大部分都是用了静态,那么面向OO的各种设计也就无法使用了。

一个问题是:当你想对MainActivity 进行单元测试,你会发现非常困难,AndroidTimeRead 必须被包含进来,如果它还引用了其他的组件(如Db 或 net),那么这些组件也必须包含入内。静态类型因为一直在内存中,如果它引用了其他类型,则被引用的对象CG无法回收。

改进

这里我们将AndroidTimeRead 进行一些改进去掉令人讨厌的静态, 将AndroidTimeRead 改成单例。
1 public classAndroidTimeRead{
2
3 private static classInstaceHolder{
4 public staticAndroidTimeReadinstance= newAndroidTimeRead();
5}
6
7 public staticAndroidTimeReadgetInstance(){
8 returnInstaceHolder.instance;
9}
10
11 privateAndroidTimeRead(){}
12
13 publicTimeviewmodelshowTime(){
14Timeviewmodelmodel= newTimeviewmodel();
15model.setTime(String.valueOf(System.currentTimeMillis()));
16 returnmodel;
17}
18
19}
MainActivitry 进行对应的

1Timeviewmodelviewmodel=AndroidTimeRead.getInstance().showTime();调用修改

这里去掉了静态的方式,可是却没有解除直接依赖实现的问题。

关注行为

设计程序时,我们应该更加关注行为而非数据,简单的理解是尽可能面向接口编程在这里例子中主要的行为就是showTime.
因此我们定义一个接口 为MainActivity 提供所需要的行为(即提供给用户的服务)。
1 public interfaceITimeService{
2TimeviewmodelshowTime();

3}

MainActivity 上的修改

1privateITimeServicetimeService;

2 // 提供注入点
3 public voidsetTimeService(ITimeServicetimeService){
4 this.timeService=timeService;
5}
6
7@Override
8 protected voidonCreate(BundlesavedInstanceState){
9 super.onCreate(savedInstanceState);
10setContentView(R.layout.activity_main);
11
12txtShowTime=(TextView)findViewById(R.id.txtShowTime);
13btnShow=(Button)findViewById(R.id.btnShow);
14btnShow.setonClickListener( newView.OnClickListener(){
15
16@Override
17 public voidonClick(Viewv){
18Timeviewmodelviewmodel=timeService.showTime();
19txtShowTime.setText(viewmodel.getTime());
20}
21});
22
23}
这里 MainActivity 引用了 ITimeService,并通过 setTimeService 的方式提供注入点(重要)。
到此一个基本的结构已经形成,当我们需要对MainActivity进行测试时,可以通过 Mock<ITimeService> 方式,并使用setTimeService 注入到MainActivity 中,解除了与具体实现的依赖。

遗憾的是上面的程序不能正常运行,ITimeService 没有实例化。我们虽然提供了注入点,但是Activity 的生命周期由系统接管,我们无法直接使用。

聪明的读者可能已经想到,我们可以通过实现一个BaseActivity(继承至Activity),然后在BaseActivity里提供IService 的实现,如 getService(class<?>),再让MainActivity 继承自BaseActivity。

事实上当你使用Roboguice 时也是需要继承自其提供的RoboActivity。

完成业务代码

在引入Roboguice 前先完成Demo的结构。添加ITimeRepository 和对应的实现,并让AndroidTimeRead 依赖 ITimeRepository。

1 public classTimeModel{
2 public longCurrentTime;
3}
4 public interfaceITimeRepository{
5TimeModelquery();
6}
ITimeRepository 的实现:
public classTimeRepository implementsITimeRepository{

@Override
publicTimeModelquery(){
TimeModelmodel= newTimeModel();
model.CurrentTime=System.currentTimeMillis();


returnmodel;
}

}

将 AndroidTimeRead 修改,让其从 ITimeRepository 中获取时间:
public classAndroidTimeRead implementsITimeService{

privateITimeRepositoryrep;

publicAndroidTimeRead(ITimeRepositoryrep){
this.rep=rep;
}

publicTimeviewmodelshowTime(){
Timeviewmodelmodel= newTimeviewmodel();
model.setTime("现在的时间是"+String.valueOf(rep.query()));
returnmodel;
}

}

可以发现,这里AndroidTimeRead 也是依赖于 ITimeRepository接口的,并且通过构造函数,提供了注入口。

新的时间获取方式的修改,并没有要求MainActivity 函数做任何修改。如果是直接使用AndroidTimeRead,则需要变更MainActivity。

引入Roboguice 应该放在哪里?

上面的代码都是与getTime() 业务相关的,而Roboguice 却是属于系统支持类。一个真正的项目中通常会包含不少这样的组件如:日志、行为打点等等。这里组件较明显的特征是与业务的关系度不大,甚至直接移除也不会影响功能的正常使用。 对于这些组件,我通常会以一种脚手架的设计方式,将它们组织起来,并为其提供系统接入点。

命名一个Infrastructure包,将需要的基础设施放置在此。

引入RoboActivity

将MainActivity 的父类修改为 RoboActivity,为View添加InjectView注入
1 public classMainActivity extendsRoboActivity{
2
3@InjectView(R.id.txtShowTime)
4 privateTextViewtxtShowTime;
5@InjectView(R.id.btnShow)
6 privateButtonbtnShow;
7
8@Inject
9 privateITimeServicetimeService;
10 // 提供注入点
11 public voidsetTimeService(ITimeServicetimeService){
12 this.timeService=timeService;
13}
14
15@Override
16 protected voidonCreate(BundlesavedInstanceState){
17 super.onCreate(savedInstanceState);
18setContentView(R.layout.activity_main);
19
20btnShow.setonClickListener( newView.OnClickListener(){
21
22@Override
23 public voidonClick(Viewv){
24Timeviewmodelviewmodel=timeService.showTime();
25txtShowTime.setText(viewmodel.getTime());
26}
27});
28
29}
由于 ITimeService 是我们自定义的服务,需要为其指定实现。
创建RoboApplication 并继承自android 的Application同时修改AndroidManifest 里的配置。创建一个TimeModule类实现Module接口。
1 public classRoboApplication extendsApplication{
2
3@Override
4 public voidonCreate(){
5 super.onCreate();
6RoboGuice.setBaseApplicationInjector( this,RoboGuice.DEFAULT_STAGE,
7RoboGuice.newDefaultRoboModule( this),newTimeModule());
8}
9}
setBaseApplicationInjector 最后一个参数是变参可以注册多个Module

1 public classTimeModule implementsModule{
2
3@Override
4 public voidconfigure(Binderbinder){
5 // 顺序无关,在具体的Activity中被创建
6 binder
7.bind(ITimeService. class)
8.to(AndroidTimeRead. class);
9 // .in(Singleton.class); // 单件
10
11binder.bind(ITimeRepository. class)
12.to(TimeRepository. class);
13
14}
15
16}

binder 用于指定接口和具体的实现的映射,
这里仍旧依赖一个问题,就是 AndroidTimeRead 对 ITimeRepository 的依赖需要指定。
这种复杂类型需要使用Provider来指定。
可以直接在 TimeModule 添加如下方法
1 @Provides
2AndroidTimeReadgetAndroidTimeRead(ITimeRepositoryrep){
3 return newAndroidTimeRead(rep);
4}
主要是通过@Provides。 除此以外还可以通过实现 Provider<T> 接口实现。
1 public classAndroidTimeReadProvider implementsProvider<AndroidTimeRead>{
2
3@Inject
4ITimeRepositoryrep;
5
6@Override
7 publicAndroidTimeReadget(){
8
9 return newAndroidTimeRead(rep);
10}
11
12}
对应的在 Module添加 AndroidTimeRead的Bind
1 @Override
2 public voidconfigure(Binderbinder){
3 // 顺序无关,在具体的Activity中被创建
4 binder
5.bind(ITimeService. class)
6.to(AndroidTimeRead. class);
7 // .in(Singleton.class); // 单件
8
9binder.bind(ITimeRepository. class)
10.to(TimeRepository. class);
11
12binder.bind(AndroidTimeRead. class)
13.toProvider(AndroidTimeReadProvider. class);
14
15}

引入注入框架需要的思考:

1、对象的生命周期如何控制:单例或 每次创建新对象?
2、框架的执行效率

3、其他可选择的框架如 dagger

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


迭代器模式(Iterator)迭代器模式(Iterator)[Cursor]意图:提供一种方法顺序访问一个聚合对象中的每个元素,而又不想暴露该对象的内部表示。应用:STL标准库迭代器实现、Java集合类型迭代器等模式结构:心得:迭代器模式的目的是在不获知集合对象内部细节的同时能对集合元素进行遍历操作
高性能IO模型浅析服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:(1)同步阻塞IO(BlockingIO):即传统的IO模型。(2)同步非阻塞IO(Non-blockingIO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的N
策略模式(Strategy)策略模式(Strategy)[Policy]意图:定义一系列算法,把他们封装起来,并且使他们可以相互替换,使算法可以独立于使用它的客户而变化。应用:排序的比较方法、封装针对类的不同的算法、消除条件判断、寄存器分配算法等。模式结构:心得:对对象(Context)的处理操作可
访问者模式(Visitor)访问者模式(Visitor)意图:表示一个作用于某对象结构中的各元素的操作,它使你在不改变各元素的类的前提下定义作用于这些元素的新操作。应用:作用于编译器语法树的语义分析算法。模式结构:心得:访问者模式是要解决对对象添加新的操作和功能时候,如何尽可能不修改对象的类的一种方
命令模式(Command)命令模式(Command)[Action/Transaction]意图:将一个请求封装为一个对象,从而可用不同的请求对客户参数化。对请求排队或记录请求日志,以及支持可撤消的操作。应用:用户操作日志、撤销恢复操作。模式结构:心得:命令对象的抽象接口(Command)提供的两个
生成器模式(Builder)生成器模式(Builder)意图:将一个对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。 应用:编译器词法分析器指导生成抽象语法树、构造迷宫等。模式结构:心得:和工厂模式不同的是,Builder模式需要详细的指导产品的生产。指导者(Director)使用C
设计模式学习心得《设计模式:可复用面向对象软件的基础》一书以更贴近读者思维的角度描述了GOF的23个设计模式。按照书中介绍的每个设计模式的内容,结合网上搜集的资料,我将对设计模式的学习心得总结出来。网络上关于设计模式的资料和文章汗牛充栋,有些文章对设计模式介绍生动形象。但是我相信“一千个读者,一千个
工厂方法模式(Factory Method)工厂方法模式(Factory Method)[Virtual Constructor]意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实力化延迟到子类。应用:多文档应用管理不同类型的文档。模式结构:心得:面对同一继承体系(Produc
单例模式(Singleton)单例模式(Singleton)意图:保证一个类只有一个实例,并提供一个访问它的全局访问点。应用:Session或者控件的唯一示例等。模式结构:心得:单例模式应该是设计模式中最简单的结构了,它的目的很简单,就是保证自身的实例只有一份。实现这种目的的方式有很多,在Java中
装饰者模式(Decorator)装饰者模式(Decorator)[Wrapper]意图:动态的给一个对象添加一些额外的职责,就增加功能来说,比生成子类更为灵活。应用:给GUI组件添加功能等。模式结构:心得:装饰器(Decorator)和被装饰的对象(ConcreteComponent)拥有统一的接口
抽象工厂模式(Abstract Factory)抽象工厂模式(Abstract Factory)[Kit]意图:提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。应用:用户界面工具包。模式结构:心得:工厂方法把生产产品的方式封装起来了,但是一个工厂只能生产一类对象,当一个工厂需要生
桥接模式(Bridge)桥接模式(Bridge)[Handle/Body]意图:将抽象部分与它的实现部分分离,使他们都可以独立的变化。应用:不同系统平台的Windows界面。模式结构:心得:用户所见类体系结构(Window派生)提供了一系列用户的高层操作的接口,但是这些接口的实现是基于具体的底层实现
适配器模式(Adapter)适配器模式(Adapter)[Wrapper]意图:将类的一个接口转换成用户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。应用:将图形类接口适配到用户界面组件类中。模式结构:心得:适配器模式一般应用在具有相似接口可复用的条件下。目标接口(Targ
组合模式(Composition)组合模式(Composition)意图:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。应用:组合图形、文件目录、GUI容器等。模式结构:心得: 用户(Client)通过抽象类(Component)提供的公用接口统一
原型模式(Prototype)原型模式(Prototype)意图:用原型实例制定创建对象的种类,并且通过拷贝这些原型创建新的对象。应用:Java/C#中的Clonable和IClonable接口等。模式结构:心得:原型模式本质上就是对象的拷贝,使用对象拷贝代替对象创建的原因有很多。比如对象的初始化构
什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。