zoukankan      html  css  js  c++  java
  • 使用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

  • 相关阅读:
    IDEA:Application Server was not connected before run configuration stop, reason: Unable to ping 1099
    Module.exports和exports的区别
    [转]aliyun阿里云Maven仓库地址——加速你的maven构建
    使用meld作为git的辅助工具
    vscode中的vue文件中emmet进行tab键不起作用
    JSON.stringify出现 "Converting circular structure to JSON"
    记Javascript一道题的理解
    Javascript类型转换的规则实例解析
    JavaScript中双叹号(!!)作用示例介绍
    typeof / instanceof / constructor / prototype
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/5061551.html
Copyright © 2011-2022 走看看