14 May 2015原文链接:http://saulmm.github.io/when-Thor-and-Hulk-meet-dagger2-rxjava-1/原文作者:Saúl Molinero
最近, 许多文章, 框架, 和android社区的讨论, 都出现关于测试和软件架构的讨论, 就像上次Droidcon Spain上说的, 我们专注于做出健壮的程序而不是去开发特定的功能. 这表明android框架和当前的android社区渐渐成熟。
今天, 如果你是一个android开发者但是仍没有听说过 Dagger 2,RxJava or Retrofit这些词, 那么你正在错过一些东西了, 这个系列的(文章)会把一些注意力放在如何用一个整洁清晰的架构观点综合使用这些框架。
我的第一个念头是只写一篇文章,但是看到这些框架提供的大量内容我决定写最少三篇系列文章。
像往常一样,所有的代码发布在 GitHub, 建议、错误和代码提交都是欢迎的,抱歉可能没有那么多时间回复所有人:)
弄明白这个框架如何工作需要一些时间, 所以我会用我已经了解的的内容使之变的清晰。
Dagger 2 基于 依赖注入 模式.
请看下面的代码片段:
// 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 我们可以这样改变之前的代码:
// Thor is awesome. He has a hammer!
public class Thor extends Avenger {
@Inject AvengerWeapon myAmazingHammer;
public void doAmazingThorWork () {
myAmazingHammer.hitSomeone();
}
}
我们没有直接访问Thor 的构造器, 注入器使用几个指令去构造Thor's hammer
public class ThorHammer extends AvengerWeapon () {
@Inject public AvengerWeapon() {
initGodHammer();
}
}
@Inject
注解用来向 Dagger 2 指明那个构造器用来创建 Thor's hammer.
Dagger 2 由Google 推广和维护,是 Square 的Dagger 分支。
首先必须配置注解器,android-apt
插件负责这个角色, 允许使用注解器但不需要插入到最终的APK文件中。 注解器同时配置由注解器生成的代码。
build.gradle
(项目根目录)
dependencies {
...
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
}
build.gradle
(模块目录)
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
...
apt 'com.google.dagger:dagger-compiler:2.0'
}
模块负责提供依赖,组件负责注入它们
举个栗子:
@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
注解的构造方法
模块被组件用来注入依赖,看这个模块的组件:
@Singleton @Component(modules = AppModule.class)
public interface AppComponent {
AvengersApplication app();
Repository dataRepository();
}
这个模块并不由 activity or fragment调用,相反,被更加复杂模块获得,提供我们需要得到的依赖。
AvengersApplication app();
Repository dataRepository();
组件必须公开他们的依赖关系图(模块提供的依赖关系),同理, 这个模块提供的依赖关系必须对其它组件可见,其它组件把当前组件作为依赖。如果这些依赖对Dagger 2不可见就不能在需要的时候被注入。
这里是我们的依赖关系树:
@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:
@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
.
我们的类 AvengersApplication
,将负责提供应用到其它组件的组件,注意,这并不以任何依赖项注入,只是提供组件。
另外需要注意的是 Dagger 2 在编译时生成必要元素,如果你没有构建项目,是找不到DaggerAppComponent
类的.
Dagger 2 通过下面的格式从你的组件中生成类: Dagger
$${YourComponent}
.
AvengersApplication.java
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
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
...
public interface AvengersComponent extends ActivityComponent {
void inject (AvengersListActivity activity);
List<Character> avengers();
}
否则依赖将不会被解决。在这种情况,presenter 会与Dagger 2提供的 Avengers 一起初始化:
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
方法。
像 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