使用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即可,更简单方便。

下载需要的包

可参考:https://github.com/roboguice/roboguice/wiki/InstallationNonMaven

项目创建

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

基本功能

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

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

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

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

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

项目包结构

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

使用静态类的实现方式

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

1 public class AndroidTimeRead {

2

3         public static TimeViewModel showTime() {

4               TimeViewModel model = new TimeViewModel();

5               model.setTime(String. valueOf(System.currentTimeMillis ()));

6                return model;

7        }

8

9 }

10

11 public class MainActivity extends Activity {

12

13         private TextView txtShowTime ;

14         private Button btnShow ;

15

16         @Override

17         protected void onCreate(Bundle savedInstanceState) {

18                super.onCreate(savedInstanceState);

19               setContentView(R.layout. activity_main);

20

21                txtShowTime = (TextView) findViewById(R.id.txtShowTime);

22                btnShow = (Button) findViewById(R.id. btnShow);

23                btnShow.setOnClickListener( new View.OnClickListener() {

24

25                       @Override

26                       public void onClick(View v) {

27                            TimeViewModel viewModel = AndroidTimeRead. showTime();

28                             txtShowTime.setText(viewModel.getTime());

29                      }

30               });

31

32        }

33

34 }

代码很简单,也实现了我们的基本需要(如果产品到此为止的话)。但有两个明显的缺点,如果项目中大部分都是用了静态,那么面向OO的各种设计也就无法使用了。

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

改进

这里我们将AndroidTimeRead 进行一些改进去掉令人讨厌的静态,将AndroidTimeRead 改成单例。

1 public class AndroidTimeRead {

2

3         private static class InstaceHolder{

4                public static AndroidTimeRead instance= new AndroidTimeRead();

5        }

6

7         public static AndroidTimeRead getInstance(){

8                return InstaceHolder.instance;

9        }

10

11         private AndroidTimeRead(){}

12

13         public TimeViewModel showTime() {

14               TimeViewModel model = new TimeViewModel();

15               model.setTime(String. valueOf(System.currentTimeMillis ()));

16                return model;

17        }

18

19 }

MainActivitry 进行对应的

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

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

关注行为

设计程序时,我们应该更加关注行为而非数据,简单的理解是尽可能面向接口编程。在这里例子中主要的行为就是showTime.

因此我们定义一个接口为MainActivity 提供所需要的行为(即提供给用户的服务)。

1 public interface ITimeService {

2        TimeViewModel showTime();

3 }

MainActivity 上的修改:

1 private ITimeService timeService ;

2         //提供注入点

3         public void setTimeService(ITimeService timeService) {

4                this.timeService = timeService;

5        }

6

7         @Override

8         protected void onCreate(Bundle savedInstanceState) {

9                super.onCreate(savedInstanceState);

10               setContentView(R.layout. activity_main);

11

12                txtShowTime = (TextView) findViewById(R.id.txtShowTime);

13                btnShow = (Button) findViewById(R.id. btnShow);

14                btnShow.setOnClickListener( new View.OnClickListener() {

15

16                       @Override

17                       public void onClick(View v) {

18                            TimeViewModel viewModel = timeService.showTime();

19                             txtShowTime.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 class TimeModel {

2     public long CurrentTime ;

3 }

4 public interface ITimeRepository {

5        TimeModel query();

6 }

ITimeRepository 的实现:

public class TimeRepository implements ITimeRepository {

@Override

public TimeModel query() {

TimeModel model=new TimeModel();

model.CurrentTime=System. currentTimeMillis();

return model;

}

}

将 AndroidTimeRead 修改,让其从 ITimeRepository 中获取时间:

public class AndroidTimeRead implements ITimeService {

private ITimeRepository rep ;

public AndroidTimeRead(ITimeRepository rep) {

this.rep = rep;

}

public TimeViewModel showTime() {

TimeViewModel model = new TimeViewModel();

model.setTime( "现在的时间是" + String.valueOf( rep.query()));

return model;

}

}

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

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

引入Roboguice  应该放在哪里?

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

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

引入RoboActivity

将MainActivity 的父类修改为 RoboActivity,为View添加InjectView注入

1 public class MainActivity extends RoboActivity {

2

3         @InjectView(R.id.txtShowTime )

4         private TextView txtShowTime ;

5         @InjectView(R.id.btnShow )

6         private Button btnShow ;

7

8         @Inject

9         private ITimeService timeService ;

10         //提供注入点

11         public void setTimeService(ITimeService timeService) {

12                this.timeService = timeService;

13        }

14

15         @Override

16         protected void onCreate(Bundle savedInstanceState) {

17                super.onCreate(savedInstanceState);

18               setContentView(R.layout. activity_main);

19

20                btnShow.setOnClickListener( new View.OnClickListener() {

21

22                       @Override

23                       public void onClick(View v) {

24                            TimeViewModel viewModel = timeService.showTime();

25                             txtShowTime.setText(viewModel.getTime());

26                      }

27               });

28

29        }

由于 ITimeService 是我们自定义的服务,需要为其指定实现。

创建RoboApplication 并继承自android 的Application同时修改AndroidManifest 里的配置。创建一个TimeModule类实现Module接口。

1 public class RoboApplication extends Application {

2

3         @Override

4         public void onCreate() {

5                super.onCreate();

6               RoboGuice. setBaseApplicationInjector(this, RoboGuice. DEFAULT_STAGE,

7                            RoboGuice. newDefaultRoboModule(this), new TimeModule());

8        }

9 }

setBaseApplicationInjector 最后一个参数是变参可以注册多个Module

1 public class TimeModule implements Module {

2

3         @Override

4         public void configure(Binder binder) {

5                //顺序无关,在具体的Activity中 被创建

6                binder

7          .bind(ITimeService. class)

8          .to(AndroidTimeRead. class);

9           //.in(Singleton.class);//单件

10

11          binder.bind(ITimeRepository. class)

12          .to(TimeRepository. class);

13

14        }

15

16 }

binder 用于指定接口和具体的实现的映射,

这里仍旧依赖一个问题,就是  AndroidTimeRead 对 ITimeRepository 的依赖需要指定。

这种复杂类型需要使用Provider来指定。

可以直接在 TimeModule 添加如下方法:

1               @Provides

2        AndroidTimeRead getAndroidTimeRead(ITimeRepository rep){

3                return new AndroidTimeRead(rep);

4        }

主要是通过@Provides。  除此以外还可以通过实现Provider<T> 接口实现。

1 public class AndroidTimeReadProvider implements Provider<AndroidTimeRead> {

2

3         @Inject

4        ITimeRepository rep;

5

6         @Override

7         public AndroidTimeRead get() {

8

9                return new AndroidTimeRead( rep );

10        }

11

12 }

对应的在 Module添加 AndroidTimeRead的Bind

1    @Override

2         public void configure(Binder binder) {

3                //顺序无关,在具体的Activity中 被创建

4                binder

5          .bind(ITimeService. class )

6          .to(AndroidTimeRead. class );

7           //.in(Singleton.class);//单件

8

9          binder.bind(ITimeRepository. class )

10          .to(TimeRepository. class );

11

12          binder.bind(AndroidTimeRead. class )

13          .toProvider(AndroidTimeReadProvider. class );

14

15        }

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

1、对象的生命周期如何控制:单例或 每次创建新对象?

2、框架的执行效率

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

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

时间: 2024-08-03 14:55:40

使用Roboguice依赖注入规划Android项目的相关文章

重新规划Android项目结构

我们需要重新规划Android项目的目录结构,分两步走: 第一步:建立AndroidLib类库,将与业务无关的逻辑转移到AndroidLib. AndroidLib中应该包括哪些业务无关的逻辑呢?应至少包括五大部分. 这几部分的说明如下: activity包中存放的是与业务无关的Activity基类.Activity基类要分两层,如图1-3所示. AndroidLib下的基类BaseActivity封装的是业务无关的公用逻辑,主项目中的AppBaseActivity基类封装的是业务相关的公用逻辑

基于ABP模块组件与依赖注入组件的项目插件开发

注意,阅读本文,需要先阅读以下两篇文章,并且对依赖注入有一定的基础. 模块系统:http://www.cnblogs.com/mienreal/p/4537522.html 依赖注入:http://www.cnblogs.com/mienreal/p/4550500.html 正文: 我最近在设计一个项目,而这个项目的一些业务功能,需要以插件的方式提供给这个项目,从而降低耦合性,主项目不会对具体业务功能产生依赖. 在以前,最简单粗暴的方式,就是扫描主程序目录下的所有dll或指定目录下的dll,然

依赖注入 gin项目的目录结构说明

前言 依赖注入的好处和特点这里不讲述了,本篇文章主要介绍gin框架如何实现依赖注入,将项目解耦. 项目结构 ├── cmd 程序入口 ├── common 通用模块代码 ├── config 配置文件 ├── controller API控制器 ├── docs 数据库文件 ├── models 数据表实体 ├── page 页面数据返回实体 ├── repository 数据访问层 ├── router 路由 ├── service 业务逻辑层 ├── vue-admin Vue前端页面代码

Spring 基础 控制反转和依赖注入

Spring框架两个最重要的知识点 1.IOC(控制反转)/DI(依赖注入):把整个项目中的所有对象交给Spring容器管理 IOC:取到对象 DI:对象的装配 依赖注入有两种方式: 1.      属性值注入方式 a.     普通类型 注入方式 b.bean注入方式 测试结果 在使用普通类型方式注入的时候,我们会有一些特殊字符,处理这种特殊字符时 我们会想使特殊字符保持原有的样子,这时候就要使用CDATA,就如上一张图片所示 2.构造参数注值方式 在一般情况,一个类会有一个默认的无参构造函数

AutoFac实现程序集级别的依赖注入

1.介绍 所谓程序集级别的依赖注入是指接口和实现的依赖不使用配置文件或硬代码实现(builder.RegisterType<UserInfoService>().As<IUserInfoService>();),而是通过名称约定实现依赖注入 2.项目接口及dll 2.1 项目结构(创建MVC3项目) 2.2 UI层需引入的dll(使用nuget引入) 2.2.1 Autofac 2.2.2 Autofac.Integration.Mvc 2.2.3 MyMVC3.Business.

从壹开始前后端分离【 .NET Core2.0 Api + Vue 3.0 + AOP + 分布式】框架之九 || 依赖注入IoC学习 + AOP界面编程初探

代码已上传Github,文末有地址 说接上文,上回说到了<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之八 || API项目整体搭建 6.3 异步泛型+依赖注入初探>,后来的标题中,我把仓储两个字给去掉了,因为好像大家对这个模式很有不同的看法,嗯~可能还是我学艺不精,没有说到其中的好处,现在在学DDD领域驱动设计相关资料,有了好的灵感再给大家分享吧. 到目前为止我们的项目已经有了基本的雏形,后端其实已经可以搭建自己的接口列表了,框架已

Gin实现依赖注入

前言 依赖注入的好处和特点这里不讲述了,本篇文章主要介绍gin框架如何实现依赖注入,将项目解耦. 项目结构 ├── cmd 程序入口 ├── common 通用模块代码 ├── config 配置文件 ├── controller API控制器 ├── docs 数据库文件 ├── models 数据表实体 ├── page 页面数据返回实体 ├── repository 数据访问层 ├── router 路由 ├── service 业务逻辑层 ├── vue-admin Vue前端页面代码

Android快速开发必备——依赖注入(DI)类库的选择ButterKnife,AndroidAnnotations,RoboGuice

关注finddreams,一起学习,一起进步:http://blog.csdn.net/finddreams/article/details/45504133 现在做移动端开发的开发者,不管是Android和IOS,公司一般都是喜欢那种具备独立开发能力的人,因为APP项目相对较小,有时候一个人完全可以单独胜任一个项目.所以如果我们要能够具备独立开发的能力,快速敏捷开发是我们所必须要去思考的事情,毕竟一个人做APP所要做的事情还是挺多的,为了项目的如期上线,我们需要用到各种第三方的库和框架,这样可

Android 开源项目android-open-project工具库解析之(一) 依赖注入,图片缓存,网络相关,数据库orm工具包,Android公共库

一.依赖注入DI 通过依赖注入减少View.服务.资源简化初始化,事件绑定等重复繁琐工作 AndroidAnnotations(Code Diet) android快速开发框架 项目地址:https://github.com/excilys/androidannotations 文档介绍:https://github.com/excilys/androidannotations/wiki 官网网址:http://androidannotations.org/ 特点:(1) 依赖注入:包括view