使用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提供服务。

  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;
       }

}
复制代码
复制代码

能够发现。这里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 








本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5061551.html,如需转载请自行联系原作者


相关文章
|
6天前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
|
5天前
|
Java Maven 开发工具
第一个安卓项目 | 中国象棋demo学习
本文是作者关于其第一个安卓项目——中国象棋demo的学习记录,展示了demo的运行结果、爬坑记录以及参考资料,包括解决Android Studio和maven相关问题的方法。
第一个安卓项目 | 中国象棋demo学习
|
23天前
|
JavaScript 前端开发 Android开发
让Vite+Vue3项目在Android端离线打开(不需要起服务)
让Vite+Vue3项目在Android端离线打开(不需要起服务)
|
23天前
|
IDE 开发工具 Android开发
安卓与iOS开发对比:平台选择对项目成功的影响
【9月更文挑战第10天】在移动应用开发的世界中,选择正确的平台是至关重要的。本文将深入探讨安卓和iOS这两大主要移动操作系统的开发环境,通过比较它们的市场份额、开发工具、编程语言和用户群体等方面,为开发者提供一个清晰的指南。我们将分析这两个平台的优势和劣势,并讨论如何根据项目需求和目标受众来做出最佳选择。无论你是初学者还是有经验的开发者,这篇文章都将帮助你更好地理解每个平台的特性,并指导你做出明智的决策。
|
23天前
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
40 8
|
20天前
|
API Android开发 iOS开发
掌握安卓与iOS应用开发中的依赖注入技术
本文探讨了在安卓和iOS应用开发中,如何有效利用依赖注入技术来提升代码的模块化、可测试性和可维护性。通过对比分析两种平台下依赖注入的实现方式与工具,本文旨在为开发者提供一套清晰、实用的依赖管理策略,助力打造高质量软件产品。
|
26天前
|
Java 开发工具 Android开发
安卓与iOS开发:平台选择对项目成功的影响
在移动应用开发的浩瀚宇宙中,安卓和iOS两大星系璀璨夺目,各自拥有独特的光芒。本文将穿梭于这两个平台之间,探讨它们在开发环境、用户群体、成本效益等方面的差异,以及这些差异如何影响一个项目的航向和终点。我们将从初学者的视角出发,逐步深入,揭示选择合适平台的重要性,以及如何根据项目需求做出明智的选择。无论你是即将启航的新手开发者,还是已经在这片星海中航行的老手,这篇文章都将为你提供有价值的导航信息。
41 2
|
1月前
|
Java 开发工具 Android开发
探索安卓与iOS开发的差异:平台选择对项目的影响
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据着重要的位置。本文旨在深入探讨这两个平台在开发过程中的主要差异,包括编程语言、开发工具、用户界面设计、性能优化以及市场分布等方面。通过对比分析,我们将揭示平台选择如何影响项目规划、执行效率和最终成果,为开发者在选择适合自己项目需求的平台时提供参考依据。
|
2月前
|
IDE 开发工具 Android开发
探索安卓与iOS开发的差异:平台选择对项目成功的影响
在移动应用开发的广阔天地中,安卓和iOS两大平台各领风骚,引领着技术进步的潮流。本文旨在深入剖析这两个平台在开发过程中的关键差异点,包括编程语言、开发工具、用户界面设计以及市场分布等方面。通过对比分析,我们不仅能更好地理解每个平台的独特优势,还能洞察这些差异如何影响项目决策和最终成果。无论你是开发者还是企业决策者,了解这些内容都将助你一臂之力,在选择适合自己项目的开发平台时做出更明智的决策。
|
2月前
|
IDE 开发工具 Android开发
探索iOS与安卓开发的差异:平台选择对项目成功的影响
【8月更文挑战第22天】在数字化时代,移动应用成为企业和个人展示创意、提供服务的重要工具。iOS和安卓作为两大主流平台,各自拥有独特的优势和限制。本文将深入探讨这两个平台在开发过程中的主要差异,以及这些差异如何影响项目规划、用户体验和市场策略。通过比较分析,旨在为开发者和企业决策者提供有价值的见解,帮助他们根据项目需求做出明智的平台选择。
下一篇
无影云桌面