当复仇者联盟遇上Dagger2、RxJava和Retrofit的巧妙结合
2015-10-16 14:04
615 查看
When the Avengers meet Dagger2, RxJava and Retrofit in a clean way
14 May 2015原文链接:http://saulmm.github.io/when-Thor-and-Hulk-meet-dagger2-rxjava-1/原文作者:SaúlMolinero
最近, 许多文章, 框架, 和android社区的讨论, 都出现关于测试和软件架构的讨论, 就像上次Droidcon Spain上说的, 我们专注于做出健壮的程序而不是去开发特定的功能. 这表明android框架和当前的android社区渐渐成熟。
今天, 如果你是一个android开发者但是仍没有听说过 Dagger 2,RxJava or Retrofit这些词,
那么你正在错过一些东西了, 这个系列的(文章)会把一些注意力放在如何用一个整洁清晰的架构观点综合使用这些框架。
我的第一个念头是只写一篇文章,但是看到这些框架提供的大量内容我决定写最少三篇系列文章。
像往常一样,所有的代码发布在 GitHub, 建议、错误和代码提交都是欢迎的,抱歉可能没有那么多时间回复所有人:)
Dependency Injectors & Dagger 2
弄明白这个框架如何工作需要一些时间, 所以我会用我已经了解的的内容使之变的清晰。Dagger 2 基于 依赖注入 模式.
请看下面的代码片段:
[code] // Thor is awesome. He has a hammer! public class Thor extends Avenger { private final AvengerWeapon myAmazingHammer; public Thor (AvengerWeapon anAmazingHammer) { myAmazingHammer = anAmazingHammer; } public void doAmazingThorWork () { myAmazingHammer.hitSomeone(); } }
Thor(雷神) 需要一个
AvengerWeapon(复仇者武器)才能工作正常, 依赖注入最基本的思想是 Thor 如果自己创建
AvengerWeapon而不是通过构造器传入就会得到很少的好处。如果
Thor 自己创建 hammer 将会增加耦合。
AvengerWeapon可以成为一个接口,根据我们的逻辑被可以有不同的实现和注入方式。
在android中, 因为框架已经设计完成。 我们不总是容易访问构造器,
Activity和
Fragment就是例子。
这些依赖注入框架像 (http://google.github.io/dagger/), Daggeror Guice 可以带来便利好处。
使用 Dagger 2 我们可以这样改变之前的代码:
[code] // Thor is awesome. He has a hammer! public class Thor extends Avenger { @Inject AvengerWeapon myAmazingHammer; public void doAmazingThorWork () { myAmazingHammer.hitSomeone(); } }
我们没有直接访问Thor 的构造器, 注入器使用几个指令去构造Thor's hammer
[code] public class ThorHammer extends AvengerWeapon () { @Inject public AvengerWeapon() { initGodHammer(); } }
@Inject注解用来向 Dagger
2 指明那个构造器用来创建 Thor's hammer.
Dagger 2
Dagger 2 由Google 推广和维护,是 Square 的Dagger 分支。首先必须配置注解器,
android-apt插件负责这个角色, 允许使用注解器但不需要插入到最终的APK文件中。 注解器同时配置由注解器生成的代码。
build.gradle(项目根目录)
[code] dependencies { ... classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' }
build.gradle(模块目录)
[code] apply plugin: 'com.neenbedankt.android-apt' dependencies { ... apt 'com.google.dagger:dagger-compiler:2.0' }
Components, modules & Avengers
模块负责提供依赖,组件负责注入它们举个栗子:
[code]@Module public class AppModule { private final AvengersApplication mAvengersApplication; public AppModule(AvengersApplication avengersApplication) { this.mAvengersApplication = avengersApplication; } @Provides @Singleton AvengersApplication provideAvengersAppContext () { return mAvengersApplication; } @Provides @Singleton Repository provideDataRepository (RestRepository restRepository) { return restRepository; } }
这个是主模块,我们感兴趣的是它的依赖存在于程序的生命周期中,一个通用的上下文和取回信息的仓库。
很简单,对吧?
我们在Dagger 2中所说的
@Provides注解,如果有需要则必会去创建其依赖,因此如果我们没有给定一个特定的依赖,Dagger 2 将会寻找有
@Inject注解的构造方法
模块被组件用来注入依赖,看这个模块的组件:
[code]@Singleton @Component(modules = AppModule.class) public interface AppComponent { AvengersApplication app(); Repository dataRepository(); }
这个模块并不由 activity or fragment调用,相反,被更加复杂模块获得,提供我们需要得到的依赖。
[code]AvengersApplication app(); Repository dataRepository();
组件必须公开他们的依赖关系图(模块提供的依赖关系),同理, 这个模块提供的依赖关系必须对其它组件可见,其它组件把当前组件作为依赖。如果这些依赖对Dagger 2不可见就不能在需要的时候被注入。
这里是我们的依赖关系树:
[code]@Module public class AvengersModule { @Provides @Activity List<Character> provideAvengers() { List<Character> avengers = new ArrayList<>(6); avengers.add(new Character( "Iron Man", R.drawable.thumb_iron_man, 1009368)); avengers.add(new Character( "Thor", R.drawable.thumb_thor, 1009664)); avengers.add(new Character( "Captain America", R.drawable.thumb_cap,1009220)); avengers.add(new Character( "Black Widow", R.drawable.thumb_nat, 1009189)); avengers.add(new Character( "Hawkeye", R.drawable.thumb_hawkeye, 1009338)); avengers.add(new Character( "Hulk", R.drawable.thumb_hulk, 1009351)); return avengers; } }
这个模块会使用一个特别的activity依赖注入,实际上负责绘制Avengers list:
[code]@Activity @Component( dependencies = AppComponent.class, modules = { AvengersModule.class, ActivityModule.class } ) public interface AvengersComponent extends ActivityComponent { void inject (AvengersListActivity activity); List<Character> avengers(); }
再次我们暴露了我们的依赖,
List<Character>给其它组件,在这种情况下出现一个新方法:
void inject (AvengersListActivity activity)。在这个方法被调用时,这些依赖将会被找到并注入到
AvengerListActivity.
All mixed
我们的类 AvengersApplication,将负责提供应用到其它组件的组件,注意,这并不以任何依赖项注入,只是提供组件。
另外需要注意的是 Dagger 2 在编译时生成必要元素,如果你没有构建项目,是找不到
DaggerAppComponent类的.
Dagger 2 通过下面的格式从你的组件中生成类:
Dagger$$
{YourComponent}.
AvengersApplication.java
[code]public class AvengersApplication extends Application { private AppComponent mAppComponent; @Override public void onCreate() { super.onCreate(); initializeInjector(); } private void initializeInjector() { mAppComponent = DaggerAppComponent.builder() .appModule(new AppModule(this)) .build(); } public AppComponent getAppComponent() { return mAppComponent; } }
AvengersListActivity.java
[code]public class AvengersListActivity extends Activity implements AvengersView { @InjectView(R.id.activity_avengers_recycler) RecyclerView mAvengersRecycler; @InjectView(R.id.activity_avengers_toolbar) Toolbar mAvengersToolbar; @Inject AvengersListPresenter mAvengersListPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_avengers_list); ButterKnife.inject(this); initializeToolbar(); initializeRecyclerView(); initializeDependencyInjector(); initializePresenter(); } private void initializeDependencyInjector() { AvengersApplication avengersApplication = (AvengersApplication) getApplication(); DaggerAvengersComponent.builder() .avengersModule(new AvengersModule()) .activityModule(new ActivityModule(this)) .appComponent(avengersApplication.getAppComponent()) .build().inject(this); }
当
initializeDependencyInjector()执行到
.inject(this)Dagger
2 开始工作并提供必要的依赖,请记住 Dagger 2 在注入时是严格的,我的意思是,可以调用组件的
inject()方法的类必须是和传入参数完全相同的类型(译者注:不能是参数类的父类和接口)。
AvengersComponent.java
[code]... public interface AvengersComponent extends ActivityComponent { void inject (AvengersListActivity activity); List<Character> avengers(); }
否则依赖将不会被解决。在这种情况,presenter 会与Dagger 2提供的 Avengers 一起初始化:
[code]public class AvengersListPresenter implements Presenter, RecyclerClickListener { private final List<Character> mAvengersList; private final Context mContext; private AvengersView mAvengersView; private Intent mIntent; @Inject public AvengersListPresenter (List<Character> avengers, Context context) { mAvengersList = avengers; mContext = context; }
Dagger 2 将会解决这个 presenter,因为它有
@Inject注解。这个构造方法的参数由 Dagger
2解决,因为它知道怎样去构建它,这得益于模块中
@Provides方法。
Conclusion
像 Dagger 2 用好了依赖注入器,其力量是无需争辩的,想象下根据框架提供的API级别你可以有不同的策略,它的可能性是无尽的。Resources:
Chiu-Ki Chan - Dagger
2 + Espresso + Mockito
Fernando Cejas - Tasting
Dagger 2 on Android
Google Developers - Dagger 2, A new
type of dependency injection
Mike Gouline - Dagger
2, Even sharper, less
相关文章推荐
- .NET和JAVA中BYTE的区别以及JAVA中“DES/CBC/PKCS5PADDING” 加密解密在.NET中的实现
- Spring Security(16)——基于表达式的权限控制
- JAVA导出EXCEL并下载
- Spring Security(15)——权限鉴定结构
- Spring+SpringMVC+mybatis+easyui整合实例(六)mybatis一对一、一对多关联查询
- Java enum枚举类型
- java 设计模式简单解析
- SpringMVC返回json数据的三种方式
- Spring定时任务的几种实现
- Java MVC + ORM框架 Nano Framework之环境搭建
- Spring 运行中 动态加载xml并实例化Bean
- hibernate session相关类的几个问题
- 利用spring el做系统日志
- java程序性能优化
- java动态代理
- 当对象类型是一个接口的时候,在Eclipse中如果跳转到该接口对应的实现类
- 关于spring3使用AOP编程时需要引入哪些jar包的问题
- Welcome to JAVA!(第五课课后练习)
- MyEclipse 10破解教程
- Spring JdbcTemplate用法整理