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

前端之家收集整理的这篇文章主要介绍了使用Roboguice依赖注入规划Android项目前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

关于依赖注入

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

@H_404_5@通常博主开发项目时喜欢以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即可,更简单方便。@H_404_5@
下载需要的包

项目创建

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

基本功能

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

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

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

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

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

项目包结构

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

使用静态类的实现方式

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

1@H_404_5@@H_404_5@public@H_404_5@@H_404_5@class@H_404_5@AndroidTimeRead{@H_404_5@

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

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

改进

这里我们将AndroidTimeRead 进行一些改进去掉令人讨厌的静态, 将AndroidTimeRead 改成单例。@H_404_5@
@H_404_5@
1@H_404_5@ public@H_404_5@ class@H_404_5@AndroidTimeRead{
2@H_404_5@
3@H_404_5@ private@H_404_5@ static@H_404_5@ class@H_404_5@InstaceHolder{
4@H_404_5@ public@H_404_5@ static@H_404_5@AndroidTimeReadinstance= new@H_404_5@AndroidTimeRead();
5@H_404_5@}
6@H_404_5@
7@H_404_5@ public@H_404_5@ static@H_404_5@AndroidTimeReadgetInstance(){
8@H_404_5@ return@H_404_5@InstaceHolder.instance;
9@H_404_5@}
10@H_404_5@
11@H_404_5@ private@H_404_5@AndroidTimeRead(){}
12@H_404_5@
13@H_404_5@ public@H_404_5@TimeviewmodelshowTime(){
14@H_404_5@Timeviewmodelmodel= new@H_404_5@Timeviewmodel();
15@H_404_5@model.setTime(String.valueOf(System.currentTimeMillis()));
16@H_404_5@ return@H_404_5@model;
17@H_404_5@}
18@H_404_5@
19@H_404_5@}
@H_404_5@
MainActivitry 进行对应的@H_404_5@@H_404_5@

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

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

关注行为

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

3@H_404_5@} @H_404_5@

MainActivity 上的修改

1@H_404_5@@H_404_5@private@H_404_5@ITimeServicetimeService;@H_404_5@

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

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

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

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

完成业务代码

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

1@H_404_5@ public@H_404_5@ class@H_404_5@TimeModel{
2@H_404_5@ public@H_404_5@ long@H_404_5@CurrentTime;
3@H_404_5@}
4@H_404_5@ public@H_404_5@ interface@H_404_5@ITimeRepository{
5@H_404_5@TimeModelquery();
6@H_404_5@}
ITimeRepository 的实现:
@H_404_5@
public@H_404_5@ class@H_404_5@TimeRepository implements@H_404_5@ITimeRepository{

@Override
public@H_404_5@TimeModelquery(){
TimeModelmodel= new@H_404_5@TimeModel();
model.CurrentTime=System.currentTimeMillis();


return@H_404_5@model;
}

}
@H_404_5@

@H_404_5@@H_404_5@
将 AndroidTimeRead 修改,让其从 ITimeRepository 中获取时间:@H_404_5@
@H_404_5@
public@H_404_5@ class@H_404_5@AndroidTimeRead implements@H_404_5@ITimeService{

private@H_404_5@ITimeRepositoryrep;

public@H_404_5@AndroidTimeRead(ITimeRepositoryrep){
this@H_404_5@.rep=rep;
}

public@H_404_5@TimeviewmodelshowTime(){
Timeviewmodelmodel= new@H_404_5@Timeviewmodel();
model.setTime("现在的时间是"+String.valueOf(rep.query()));
return@H_404_5@model;
}

}
@H_404_5@

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

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

引入Roboguice 应该放在哪里?

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

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

引入RoboActivity

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

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

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

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

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

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

猜你在找的设计模式相关文章