前言

好久没写博客了,罪过啊~记事本里累积了不少东西,整理整理放上来。

关于依赖注入

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 }
 
 public class MainActivity extends Activity {
 
         private TextView txtShowTime ;
         private Button btnShow ;
 
         @Override
         protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
               setContentView(R.layout. activity_main);
 
                txtShowTime = (TextView) findViewById(R.id.txtShowTime);
                btnShow = (Button) findViewById(R.id. btnShow);
                btnShow.setOnClickListener( new View.OnClickListener() {
 
                       @Override
                       public void onClick(View v) {
                            TimeViewModel viewModel = AndroidTimeRead. showTime();
                             txtShowTime.setText(viewModel.getTime());
                      }
               });
 
        }
 
 }
  代码很简单,也实现了我们的基本需要(如果产品到此为止的话)。但有两个明显的缺点,如果项目中大部分都是用了静态,那么面向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        }
        
         private AndroidTimeRead(){}
        
         public TimeViewModel showTime() {
               TimeViewModel model = new TimeViewModel();
               model.setTime(String. valueOf(System.currentTimeMillis ()));
                return model;
        }
 
 }
MainActivitry 进行对应的

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

 

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

关注行为

     设计程序时,我们应该更加关注行为而非数据,简单的理解是尽可能面向接口编程。在这里例子中主要的行为就是showTime.
因此我们定义一个接口为MainActivity 提供所需要的行为(即提供给用户的服务)。
 public interface ITimeService {
        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);
               setContentView(R.layout. activity_main);
 
                txtShowTime = (TextView) findViewById(R.id.txtShowTime);
                btnShow = (Button) findViewById(R.id. btnShow);
                btnShow.setOnClickListener( new View.OnClickListener() {
 
                       @Override
                       public void onClick(View v) {
                            TimeViewModel viewModel = timeService.showTime();
                             txtShowTime.setText(viewModel.getTime());
                      }
               });
 
        }
这里 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。

 public class TimeModel {
     public long CurrentTime ;
 }
 public interface ITimeRepository {
        TimeModel query();
 }
   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 ;
         //提供注入点
         public void setTimeService(ITimeService timeService) {
                this.timeService = timeService;
        }
        
         @Override
         protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
               setContentView(R.layout. activity_main);
 
                btnShow.setOnClickListener( new View.OnClickListener() {
 
                       @Override
                       public void onClick(View v) {
                            TimeViewModel viewModel = timeService.showTime();
                             txtShowTime.setText(viewModel.getTime());
                      }
               });
 
        }
由于 ITimeService 是我们自定义的服务,需要为其指定实现。
创建RoboApplication 并继承自android 的Application同时修改AndroidManifest 里的配置。创建一个TimeModule类实现Module接口。
 public class RoboApplication extends Application {
 
         @Override
         public void onCreate() {
                super.onCreate();
               RoboGuice. setBaseApplicationInjector(this, RoboGuice. DEFAULT_STAGE,
                            RoboGuice. newDefaultRoboModule(this), new TimeModule());
        }
 }
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);//单件
         
          binder.bind(ITimeRepository. class)
          .to(TimeRepository. class);
 
        }
 
 }
binder 用于指定接口和具体的实现的映射,
这里仍旧依赖一个问题,就是  AndroidTimeRead 对 ITimeRepository 的依赖需要指定。
这种复杂类型需要使用Provider来指定。
可以直接在 TimeModule 添加如下方法:
 1               @Provides
        AndroidTimeRead getAndroidTimeRead(ITimeRepository rep){
                return new AndroidTimeRead(rep);
        }
主要是通过@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 );
        }
 
 }
对应的在 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 )
          .to(TimeRepository. class );
         
          binder.bind(AndroidTimeRead. class )
          .toProvider(AndroidTimeReadProvider. class );
 
        }
      

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

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

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

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

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

    注意,阅读本文,需要先阅读以下两篇文章,并且对依赖注入有一定的基础. 模块系统:http://www.cnblogs.com/mienreal/p/4537522.html 依赖注入:http://w ...

  2. 在 xunit 测试项目中使用依赖注入

    在 xunit 测试项目中使用依赖注入 Intro 之前写过几篇 xunit 依赖注入的文章,今天这篇文章将结合我在 .NET Conf 上的分享,更加系统的分享一下在测试中的应用案例. 之所以想分享 ...

  3. MVC3+AutoFac实现程序集级别的依赖注入

    1.介绍      所谓程序集级别的依赖注入是指接口和实现的依赖不使用配置文件或硬代码实现(builder.RegisterType<UserInfoService>().As<IU ...

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

    更新 1.如果看不懂本文,或者比较困难,先别着急问问题,我单写了一个关于依赖注入的小Demo,可以下载看看,多思考思考注入的原理: https://github.com/anjoy8/BlogArti ...

  5. Ioc 器管理的应用程序设计,前奏:容器属于哪里? 控制容器的反转和依赖注入模式

    Ioc 器管理的应用程序设计,前奏:容器属于哪里?   我将讨论一些我认为应该应用于“容器管理”应用程序设计的原则. 模式1:服务字典 字典或关联数组是我们在软件工程中学到的第一个构造. 很容易看到使 ...

  6. 【半小时大话.net依赖注入】(一)理论基础+实战控制台程序实现AutoFac注入

    系列目录 第一章|理论基础+实战控制台程序实现AutoFac注入 第二章|AutoFac的常见使用套路 第三章|实战Asp.Net Framework Web程序实现AutoFac注入 第四章|实战A ...

  7. PHP 在Swoole中使用双IoC容器实现无污染的依赖注入

    简介: 容器(container)技术(可以理解为全局的工厂方法), 已经是现代项目的标配. 基于容器, 可以进一步实现控制反转, 依赖注入. Laravel 的巨大成功就是构建在它非常强大的IoC容 ...

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

    本文梯子 本文3.0版本文章 更新 代码已上传Github+Gitee,文末有地址 零.今天完成的绿色部分 一.依赖注入的理解和思考 二.常见的IoC框架有哪些 1.Autofac+原生 2.三种注入 ...

  9. Gin实现依赖注入

    前言 依赖注入的好处和特点这里不讲述了,本篇文章主要介绍gin框架如何实现依赖注入,将项目解耦. 项目结构 ├── cmd 程序入口 ├── common 通用模块代码 ├── config 配置文件 ...

随机推荐

  1. html导入css样式的方法

    在html网页中引入css样式表主要有一下四种方法 1.行内引入 <p style="width:100px;height:40px;color:red;"></ ...

  2. hdu 5876 (补图BFS) Sparse Graph

    题目:这里 题意: 相当于一开始给一个初始好了的无向完全图给你,然后给让你删除m条边,再给你一个点v,最后问你在剩下的图里从这个点v出发能到达所有边点的最小路径是多少? 一看是所有点的最小路径,一看就 ...

  3. fiddler的前端资源代理功能。

       说一个很有用的东西.fiddler的autoResponder功能能把线上网站的资源引用代理到本地.比如这个js.我们改了想测测效果.但是如果经过中间的流程要把这个文件发布到线上去挺麻烦的,而且 ...

  4. FreeSWITCH 1.2.5.3 Step by Step Install

    Ubuntu: apt-get -y install build-essential automake autoconf git-core wget libtool apt-get -y instal ...

  5. linux修改主机名称

    http://blog.csdn.net/qq_20480611/article/details/51017033 ========================================== ...

  6. modesim测试语句

    : 'd2; Reg2 <= Reg1; i <= i + 1'b1; join : 'd2; i <= i + 1'b1; join : 'd2; Reg2 <= Reg1; ...

  7. DbProviderFactory .net数据库工厂模式

    http://kb.cnblogs.com/page/72789/  工厂模式 http://www.cnblogs.com/Ruiky/archive/2012/04/19/2456784.html ...

  8. Some About Spring

    什么是Spring:Spring是一个从实际开发中抽取出来的框架,它对代码中需要重复解决的步骤抽象成为了一个框架.留给开发者的仅仅是与特定应用相关的部分,大大提高了企业应用的开发效率.例外.Sprin ...

  9. iOS NSDate与NSString相互转化

    1.时间格式的字符串转date NSString *birthdayStr=@"1986-03-28 00:00:00.000"; NSDateFormatter *dateFor ...

  10. android download manager

    下载管理器,有个哥们写得很好了http://www.trinea.cn/android/android-downloadmanager/ 下载后台通知 下载管理器内容交互 最近对内部业务逻辑整理了一下 ...