Dagger Android支持库(译文)
与其他依赖注入框架相比,Dagger 2 最大的优点是他不使用反射,严格的生成实现类,这意味着他可以使用在 Android 应用上。但是在Android上使用仍有一些注意事项。
使用Dagger编写Android应用程序的一个主要困难是,许多Android框架类都由操作系统本身实例化,如Activity和Fragment,但如果Dagger可以创建所有注入的对象,则效果最佳。
相反,您必须在生命周期方法中执行成员注入。 这意味着许多类最终看起来像:
public class FrombulationActivity extends Activity {
@Inject Frombulator frombulator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// DO THIS FIRST. Otherwise frombulator might be null!
((SomeApplicationBaseType) getContext().getApplicationContext())
.getApplicationComponent()
.newActivityComponentBuilder()
.activity(this)
.build()
.inject(this);
// ... now you can write the exciting code
}
}
这有一些问题:
- 复制粘贴代码使得以后很难重构。 随着越来越多的开发人员复制粘贴该块,更少的人会知道它实际上做了什么。
- 更重要的是,它需要请求注入类型(FrombulationActivity)来了解其注入器。 即使这是通过接口而不是具体类型完成的,它也打破了依赖注入的核心原则:类不应该知道它是如何注入的。
dagger.android中的类提供了一种简化此模式的方法。
注入 Activity 对象
- 在应用程序组件中安装 AndroidInjectionModule,以确保这些基本类型所需的所有绑定都可用。
- 首先编写实现AndroidInjector 的@Subcomponent,并使用扩展AndroidInjector.Builder 的@ Subcomponent.Builder:
@Subcomponent(modules = ...)
public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<YourActivity> {}
}
- 定义子组件后,通过定义绑定子组件构建器的模块并将其添加到注入应用程序的组件,将其添加到组件层次结构中:
@Module(subcomponents = YourActivitySubcomponent.class)
abstract class YourActivityModule {
@Binds
@IntoMap
@ActivityKey(YourActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder);
}
@Component(modules = {..., YourActivityModule.class})
interface YourApplicationComponent {}
专业提示:如果你的 subcomponent 和他的 builder 没有其他方法或者超类型,而不是步骤2中提到的方法或超类型,则可以使用 @ContributesAndroidInjector 为您生成它们。 而不需要第2步和第3步,添加一个返回活动的抽象模块方法,使用@ContributesAndroidInjector对其进行注释,并指定要安装到子组件中的模块。 如果子组件需要范围,则还要将范围注释应用于该方法。
@ActivityScope
@ContributesAndroidInjector(modules = { /* modules to install into the subcomponent */ })
abstract YourActivity contributeYourActivityInjector();
- 接下来,让 Application 实现 HasActivityInjector 接口和 注入@Inject a DispatchingAndroidInjector ,实现 activityInjector() 方法:
public class YourApplication extends Application implements HasActivityInjector {
@Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerYourApplicationComponent.create()
.inject(this);
}
@Override
public AndroidInjector<Activity> 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 并将 activity 传递给 inject(Activity)。DispatchingAndroidInjector 为您的活动类(即YourActivitySubcomponent.Builder)查找AndroidInjector.Factory,创建AndroidInjector(即YourActivitySubcomponent),并将您的活动传递给 inject(YourActivity)。
注入 Fragment 对象
注入 Fragment 就像注入 Activity 一样。以相同的方式定义 subcomponent,将 Activity 类型参数替换为 Fragment,将 @ActivityKey 替换为 @FragmentKey,将 HasActivityInjector 替换为 HasFragmentInjector。
像在 Activity 中 onCreate() 注入一样,Fragment 在 onAttach() 注入。
与为 Activitys 定义的模块不同,您可以选择在何处安装 Fragments 模块。 您可以将Fragment组件作为另一个Fragment组件,Activity组件或Application组件的子组件 – 这一切都取决于Fragment所需的其他绑定。 在确定组件位置后,使相应的类型实现 HasFragmentInjector。 例如,如果您的 Fragment 需要来自 YourActivitySubcomponent 的绑定,那么您的代码将如下所示:
public class YourActivity extends Activity
implements HasFragmentInjector {
@Inject DispatchingAndroidInjector<Fragment> fragmentInjector;
@Override
public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
// ...
}
@Override
public AndroidInjector<Fragment> 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<YourFragment> {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<YourFragment> {}
}
@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 { ... }
基本类型支持
因为 DispatchingAndroidInjector 在运行时通过类查找适当的 AndroidInjector.Factory,所以基类可以实现HasActivityInjector / HasFragmentInjector / etc以及调用AndroidInjection.inject()。 所有每个子类需要做的是绑定相应的 @Subcomponent。 如果您没有复杂的类层次结构,Dagger提供了一些基本类型,例如DaggerActivity和 DaggerFragment。 Dagger 还为同一目的提供了 DaggerApplication – 您需要做的就是扩展它并覆盖applicationInjector()方法以返回应该注入 Application 的组件。
也同时包括如下类型:
- DaggerService 和 DaggerIntentService
- DaggerBroadcastReceiver
- DaggerContentProvider
注意:只有在AndroidManifest.xml中注册BroadcastReceiver时才应使用DaggerBroadcastReceiver。 在您自己的代码中创建BroadcastReceiver时,使用构造函数注入。
支持库
对于Android支持库的用户,dagger.android.support 包中存在相同类型。请注意,虽然支持Fragment用户必须绑定AndroidInjector.Factory <? extends android.support.v4.app.Fragment>,对于AppCompat,用户应该继续实现AndroidInjector.Factory <? extends Activity> 而不是 <? extends AppCompatActivity>(或FragmentActivity)。
集成方式如下:
dependencies {
compile 'com.google.dagger:dagger-android:2.x'
compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
}
注入时机
尽可能优先使用构造函数注入,因为 javac 将确保在设置之前不引用任何字段,这有助于避免NullPointerExceptions。 当需要注射成员时(如上所述),优选尽早注射。 出于这个原因,DaggerActivity 在调用 super.onCreate() 之前立即在 onCreate() 中调用 AndroidInjection.inject(),而 DaggerFragment 在 onAttach() 中执行相同操作,这也可以防止重新附加Fragment时出现不一致。
在Activity中的super.onCreate() 之前调用AndroidInjection.inject() 是至关重要的,因为对 super 的调用会在配置更改期间附加前一个活动实例中的 Fragments,从而注入 Fragments。 为了使Fragment注入成功,必须已经注入了Activity。 对于 ErrorProne 的用户,在 super.onCreate() 之后调用 AndroidInjection.inject() 是一个编译器错误。
问题
AndroidInjector.Factory 范围:
AndroidInjector.Factory 旨在成为无状态接口,因此实现者不必担心管理与将要注入的对象相关的状态。当DispatchingAndroidInjector 请求 AndroidInjector.Factory 时,它通过 Provider 执行此操作,以便它不会显式保留工厂的任何实例。 因为Dagger生成的 AndroidInjector.Builder 实现确实保留了正在注入的 Activity / Fragment / etc的实例,所以将范围应用于提供它们的方法是一个编译时错误。 如果您肯定您的 AndroidInjector.Factory 没有为注入的对象保留实例,则可以通过将 @SuppressWarnings(“dagger.android.ScopedInjectoryFactory”) 应用于模块方法来抑制此错误。