当前位置 : 主页 > 网络编程 > 其它编程 >

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

来源:互联网 收集:自由互联 发布时间:2023-07-02
关于依赖注入 DependencyInjection(依赖注入)可以很好的帮助我们分离模块,降低耦合、提高可测试性。(PS:Roboguice只是一个工具,依赖注入更多的是一种 关于依赖注入 Dependency Inject
关于依赖注入 DependencyInjection(依赖注入)可以很好的帮助我们分离模块,降低耦合、提高可测试性。(PS:Roboguice只是一个工具,依赖注入更多的是一种 关于依赖注入 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         @Override17         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                       @Override26                       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                       @Override17                       public void onClick(View v) {18                            TimeViewModel viewModel = timeService.showTime();19                             txtShowTime.setText(viewModel.getTime());20                      }21               });22 23        },这里 MainActivity 引用了 ITimeService,并通过  setTimeService 的方式提供注入点(重要)。到此一个基本的结构已经形成,当我们需要对MainActivity进行测试时,可以通过  Mock 方式,并使用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         @Override16         protected void onCreate(Bundle savedInstanceState) {17                super.onCreate(savedInstanceState);18               setContentView(R.layout. activity_main);19 20                btnShow.setOnClickListener( new View.OnClickListener() {21 22                       @Override23                       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         @Override4         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               @Provides2        AndroidTimeRead getAndroidTimeRead(ITimeRepository rep){3                return new AndroidTimeRead(rep);4        }主要是通过@Provides。  除此以外还可以通过实现Provider 接口实现。, 1 public class AndroidTimeReadProvider implements Provider { 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项目,,

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

上一篇:OAuth2.0系列六:OAuth2.0四种授权模式总结
下一篇:没有了
网友评论