您的位置:首页 > 产品设计 > UI/UE

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

2015-12-20 18:46 423 查看

关于依赖注入

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就可以。更简单方便。

[b]下载须要的包[/b]
可參考: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 }



代码非常easy,也实现了我们的基本须要(假设产品到此为止的话)。

但有两个明显的缺点,假设项目中大部分都是用了静态。那么面向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;

}

}



[align=left]能够发现。这里AndroidTimeRead 也是依赖于 ITimeRepository接口的。而且通过构造函数。提供了注入口。[/align]

[align=left]新的时间获取方式的改动,并没有要求MainActivity 函数做不论什么改动。假设是直接使用AndroidTimeRead,则须要变更MainActivity。[/align]

引入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 }



[align=left]setBaseApplicationInjector 最后一个參数是变參能够注冊多个Module[/align]



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 }



[align=left]binder 用于指定接口和详细的实现的映射,[/align]
[align=left]这里仍旧依赖一个问题,就是 AndroidTimeRead 对 ITimeRepository 的依赖须要指定。[/align]
[align=left]这样的复杂类型须要使用Provider来指定。[/align]
[align=left]能够直接在 TimeModule 加入例如以下方法:[/align]

[align=left] 1 @Provides[/align]

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 }



[align=left]相应的在 Module加入 AndroidTimeRead的Bind[/align]

[align=left] 1 @Override[/align]



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 }



[align=left] [/align]



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

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

2、框架的运行效率

3、其它可选择的框架如 dagger
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: