前言
最近看Google新的框架sample时android-architecture-components发现了dagger2在Android平台上新的写法,很简洁,值得学习。特意看了看dagger2的官方文档Android部分,讲得很详细也很到位。因为是新姿势,中文的学习资料还比较少,所以我决定翻译一下官方文档。以下是主要是对dagger2 Android部分官方文档的翻译,并增加了一些内容帮助理解。文档其它部分的已经有人翻译过了dagger2官方文档中文。如果你还不了解dagger2,或者不太熟悉dagger2中的subcomponent和multibindings的话(以下内容需要熟悉这些内容才能看懂),可以先看一下dagger2官方文档中文。dagger2的学习曲线还是很陡峭的,官方文档永远是最好的学习资料。
Android Architecture Components是2017年Google I/O 大会上新推出的应用框架,主要用于解决UI组件的生命周期和数据持久化问题,帮助我们轻易地处理配置变化(像屏幕旋转)时数据的存储问题。构建感知生命周期的Observer,防止内存泄漏。非常值得学习。
Architecture Components官方文档
中文翻译
我已经放弃使用dagger.android了,具体原因可以查看当定义Dagger2 Scope时,你在定义什么?
开始
相较于其它的依赖注入框架而言,Dagger2其中一个主要的优势是,它不使用反射来生成其实现。这意味着它可以被用在Android应用上(意思就是不影响APP的性能)。然而,把Dagger2应用在Android平台上,还是有一些点需要注意。
dagger.android
在Android平台上使用Dagger的一个主要的不同是,很多类的实例化依赖于操作系统本身,像是Activity和Fragment,但是Dagger最理想的工作方式是它能够构造所有需要注入的类的实例。所以,你必须在它们(Activity、Fragment等)的生命周期中进行成员的注入。很多类看上去跟下面类似:
public class FrombulationActivity extends Activity {
@Inject Frombulator frombulator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//先写如下代码, 否则frombulator可能为null!
((SomeApplicationBaseType) getContext().getApplicationContext())
.getApplicationComponent()
.newActivityComponentBuilder()
.activity(this)
.build()
.inject(this);
// ...其它代码
}
}
这么做有以下问题:
- 复制粘贴同样的代码使得以后想要重构变得困难。越来越多的这样复制粘贴的代码,开发者反而对这段代码的作用了解的更少。
- 更加重要的是,它需要被注入类(FrombulationActivity)知道它的注入类。即使这是通过接口实现的,而不是具体的类。但是,这仍然破坏了依赖注入的的核心原则:一个类不应该对它是如何被注入的有任何的了解。
补充说明:以上这样的代码其实在Android平台上并不常见,更常见的是类似如下的代码:
@Singleton
@Component(modules= AppModule.class)
public interface AppComponent {
Application getApplication();
//其它需要暴露出来的类,供dependencies使用
}
@ActivityScope
@Component(dependencies = AppComponent::class, modules= ActivityModule.class)
interface ActivityComponent {
void inject(YourActivity activity)
}
public class YourActivity extends Activity {
@Inject Frombulator frombulator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerActivityComponent.builder()
.appComponent(YourApp.getAppComponent())
.activityModule(new ActivityModule(this))
.build()
.inject(this);
// ...其它代码
}
}
也就是通过component之间依赖(dependencies)的方式来管理不同component之间的关系;而Google文档上的例子是通过子组件(subcomponent)的方式来管理component间的关系,并且如果想使用本文所介绍的新的姿势,就必须使用子组件的方式,抛弃原来依赖的方式。就管理component之间的关系而言,这两种方式都是可以的,我也一直都是使用依赖的方式来构建不同的component,这么做相较于子组件的方式而言,最大的优势就是简单,直接依赖另外一个component,被依赖的component暴露出相应的类即可。而子组件的方式写起来比较麻烦。关键是还得额外学习subcomponent的是怎么回事,光整明白component就够累的了,还要啥subcomponent。 But,解锁了这篇文章介绍的dagger2和Android结合的新姿势,老司机就需要考虑这么一个问题了,需不需要换个姿势呢?!我个人觉得,还是有必要的,原因如下:
- 符合依赖注入的的核心原则:一个类不应该对它是如何被注入的有任何的了解。这个核心原则体现在我们的代码上就是,在Activity、Fragment等类需要注入对象时,可以直接使用@Inject注解一个对象即可,不需要生成component(subcomponent)了,使用更加的方便。
- 我们都是有追求的老司机,借此学习一下subcomponent的使用有益无害。
- 代码量更少。
没有最懒的程序猿,只有更懒得程序猿。
Activity的注入
- 在你的Application Component中加入AndroidInjectionModule模块,以提供所有基本类型的绑定。
@Singleton
@Component(modules= {
AndroidInjectionModule.class,
...
})
public interface AppComponent {
...
}
AndroidInjectionModule并没有什么特别的,只是一个普通的module,该module提供了5个Map,Map的key是Android四大组件和Fragment的class对象,Map的value是相应的注入器的工厂方法。
@Module
public abstract class AndroidInjectionModule {
@Multibinds
abstract Map, AndroidInjector.Factory extends Activity>>
activityInjectorFactories();
@Multibinds
abstract Map, AndroidInjector.Factory extends Fragment>>
fragmentInjectorFactories();
@Multibinds
abstract Map, AndroidInjector.Factory extends Service>>
serviceInjectorFactories();
@Multibinds
abstract Map, AndroidInjector.Factory extends BroadcastReceiver>>
broadcastReceiverInjectorFactories();
@Multibinds
abstract Map, AndroidInjector.Factory extends ContentProvider>>
contentProviderInjectorFactories();
private AndroidInjectionModule() {}
}
- 声明你的subcomponent并且实现接口AndroidInjector
,该subcomponent需要有一个被@Subcomponent.Builder注解的并扩展自AndroidInjector.Builder 的构造器:
@Subcomponent(modules = ...)
public interface YourActivitySubcomponent extends AndroidInjector {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder {}
}
- 声明过subcomponent之后,把它通过如下方式加入到主component体系中:定义一个提供该subcomponent builder的module,并且把该module加入到你的AppComponent中。
@Module(subcomponents = YourActivitySubcomponent.class)
abstract class YourActivityModule {
@Binds
@IntoMap
@ActivityKey(YourActivity.class)
abstract AndroidInjector.Factory extends Activity>
bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder);
}
@Singleton
@Component(modules = {
AndroidInjectionModule.class,
YourActivityModule.class,...
})
interface AppComponent {}
注意:如果你的subcomponent和它的builder除了第2步中提及的方法或者超类没有其它的内容,你可以用 @ContributesAndroidInjector生成2、3步中的一切。现在不需要步骤2和3,你只需声明一个abstract module,返回你所需的activity(用 @ContributesAndroidInjector注解),可以声明subcomponent需要的其它的module。如果这个subcomponent需要scope注解,也可以声明:
@Module
public abstract class ActivityBulidersModule {
@ActivityScope
@ContributesAndroidInjector(modules = {/*subcomponent需要的module*/})
abstract YourActivity contributeYourActivity();
}
@Singleton
@Component(modules = {
AndroidInjectionModule.class,
ActivityBulidersModule.class,...
})
interface AppComponent {}
@ContributesAndroidInjector注解是dagger-android-2.11中提供的,它会生成如下代码:
@Module(subcomponents = ActivityBulidersModule_YourActivityInjector.YourActivitySubcomponent.class)
public abstract class ActivityBulidersModule_YourActivityInjector {
private ActivityBulidersModule_YourActivityInjector() {}
@Binds
@IntoMap
@ActivityKey(YourActivity.class)
abstract AndroidInjector.Factory extends Activity> bindAndroidInjectorFactory(
YourActivitySubcomponent.Builder builder);
@ActivityScope
@Subcomponent(modules = {/*subcomponent需要的module*/})
public interface YourActivitySubcomponent extends AndroidInjector {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder {}
}
}
是不是跟我们自己写的一毛一样,@ContributesAndroidInjector只是帮我们自动生成了一些代码,并没有什么特别的,但前提是第2步没有其它方法或者超类型。
- 下一步,让你的Application实现HasActivityInjector并且@Inject DispatchingAndroidInjector
而后从方法activityInjector()(接口HasActivityInjector中的方法)返回:
public class YourApplication extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector dispatchingActivityInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.create()
.inject(this);
}
@Override
public AndroidInjector activityInjector() {
return dispatchingActivityInjector;
}
}
- 最后,在 Activity.onCreate() 方法中在super.onCreate()之前调用AndroidInjection.inject(this)
public class YourActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
}
- 恭喜你!完成了!
如何工作的?
AndroidInjection.inject() 从Application中获取了一个 DispatchingAndroidInjector
更多关于实现的原理请查看我的另外一篇文章Dagger2在Android平台上的新魔法
Fragment注入
注入Fragment就跟注入Activity一样。以相同的方式定义subcomponent,把Activity类型替换为Fragment,@ActivityKey替换为@FragmentKey,HasActivityInjector替换为HasFragmentInjector。
和Activity在onCreate()中注入不同,Fragment的注入在方法onAttach()中。
和为Activity添加module不同,在Fragment中你可以选择在哪添加module。你可以把你的Fragment的subcomponent声明为另一个Fragment component的子component,或者是Activity component的子component,或者是Application component的子component——这取决于你想在哪注入你的subcomponent。在决定了Fragment subcomponent是哪个component的子component之后,让相应的类型实现接口HasFragmentInjector。
原文说的很绕,其实上面一段话的核心意思就是,Fragment的subcomponent可以是Fragment、Activity、Application component(或subcomponent)的subcomponent,只要其对应的类型(Fragment、Activity、Application)实现了HasFragmentInjector接口即可。作为对比,Activity则只能是Application component的subcomponent,所以只能是Application实现HasActivityInjector接口。
例如,你的Fragment subcomponent是YourActivitySubcomponent的子component。你的代码类似于此:
public class YourActivity extends Activity
implements HasFragmentInjector {
@Inject
DispatchingAndroidInjector fragmentInjector;
@Override
public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
// ...
}
@Override
public AndroidInjector fragmentInjector() {
return fragmentInjector;
}
}
public class YourFragment extends Fragment {
@Inject SomeDependency someDep;
@Override
public void onAttach(Activity activity) {
AndroidInjection.inject(this);
super.onAttach(activity);
// ...
}
}
@Subcomponent(modules = ...)
public interface YourFragmentSubcomponent extends AndroidInjector {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder {}
}
@Module(subcomponents = YourFragmentSubcomponent.class)
abstract class YourFragmentModule {
@Binds
@IntoMap
@FragmentKey(YourFragment.class)
abstract AndroidInjector.Factory extends Fragment>
bindYourFragmentInjectorFactory(YourFragmentSubcomponent.Builder builder);
}
@Subcomponent(modules = { YourFragmentModule.class, ... }
public interface YourActivityOrYourApplicationComponent { ... }
同样我们可以简写:
@Module
public abstract class FragmentBuildersModule {
@ContributesAndroidInjector
abstract YourFragment contributeYourFragment();
}
Support libraries
对于使用Android support library的用户,有dagger.android.support提供了支持。注意用支持库中的Fragment,应该绑定AndroidInjector.Factory extends android.support.v4.app.Fragment>,但是仍应该实现AndroidInjector.Factory extends Activity> 而不是 extends AppCompatActivity> (或者 FragmentActivity)。dagger.android.support中有AndroidSupportInjectionModule,提供了对android.support.v4.app.Fragment的支持:
@Module(includes = AndroidInjectionModule.class)
public abstract class AndroidSupportInjectionModule {
@Multibinds
abstract Map, AndroidInjector.Factory extends Fragment>>
supportFragmentInjectorFactories();
private AndroidSupportInjectionModule() {}
}
如何获取
在你的build.gradle中增加
dependencies {
compile 'com.google.dagger:dagger-android:2.x'
compile 'com.google.dagger:dagger-android-support:2.x' // 如果你使用support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
}
参考:
Dagger&Android