Dagger2相比于其他大多数依赖注入框架的主要优势就是它是严格是实现(没有通过反射),那意味着它可以用在Android应用中。然而,在Android应用中使用Dagger还是有一些需要考虑的地方。
理念
虽然为Android编写的代买是Java源码,他在风格上常常有很大的不同。通常,存在这样的差异是出狱移动平台的独特性能考虑。
但是通畅应用于Android代码的许多模式与应用于其他Java代码的模式相反。在Effect Java中的许多建议也被认为不适合Android。
为了实现惯用代码的习惯性用法和可移植性,Dagger依赖proGuard来对编译后的字节码进行后处理。这使得Dagger可以生成在服务器和Android上看起来很自然的源码,同时使用不同的工具链生成在两种环境都能高效执行的字节码。再者,Dagger有一个明确的目标就是确保其生成的Java源代码始终与ProGuard优化兼容。
当然,并不是所有的问题都能以上述的那种方式解决,但这是提供Android特定兼容性的主要机制。
tl;dr
Dagger假设Android用户将使用ProGuard
为什么在Android中使用Dagger很难
使用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
}
}
这样存在一些问题:
1.复制粘贴代码使得以后很难重构。随着越来越多的开发者使用复制张贴块(这不是在说我吗,捂脸),知道它实际功能的人就越来越少了。
2.更重要的是,他要求请求注入的类型(FrombulationActivity)知道他的注入器,即使通过接口而不是具体类型完成此操作,他也会破坏依赖注入的核心原理:一个类不应该知道它是如何注入的。
dagger.android
dagger.android中的classes提供一种方法来简化上述问题.这需要学习一些额外的API和概念,给你提供了简化的样板和注入方式,在你的Android类中。
另一个种方法是只是用常规的Dagger API,并遵循此处的指南,这可能更容易理解但缺点是需要手动编写额外的样板。
Jetpack和Dagger团队正在合作开发一款基于Android的Dagger新项目,希望能在很大程度上改变现状.不幸的是目前还没有完成,如何在当下选择在你的Android项目中使用Dagger时,可能需要考虑这一点.
注入Activity对象
1.在你的Application component中安装AndroidInjectionModule来确保这些基本类型所需要的所有绑定都可用。
2.首先用@Subcomponent注解注释一个实现AndroidInjector的YourActivitySubcomponent接口,用@Subcompont.Factory注解注解继承了AndroidInjector.Factory的Factory内部接口:
@Subcomponent(modules = ...)
public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
@Subcomponent.Factory
public interface Factory extends AndroidInjector.Factory<YourActivity> {}
}
3.用@Module注解注解一个YourActivityModule,绑定了subcomponent Factory,subcomponents指向上面所写的YourActivitySubComponent,将其添加到YourApplicationComponent注入你的Application中:
@Module(subcomponents = YourActivitySubcomponent.class)
abstract class YourActivityModule {
@Binds
@IntoMap
@ClassKey(YourActivity.class)
abstract AndroidInjector.Factory<?>
bindYourAndroidInjectorFactory(YourActivitySubcomponent.Factory factory);
}
@Component(modules = {..., YourActivityModule.class})
interface YourApplicationComponent {}
小提示:如果你的subcomponent和其Factory没有有第2步中提到的方法或者超类型,你可以使用@ContributesAndroidInjcetor来生成它们。而不是步骤2和3中,通过抽象的module方法 给你的Activity,用@ContributesAndroidInjector注视她,并在你想要的安装的Subcomponent中指定module,如果Subcomponent需要作用域,使用@Scope注解注视到方法上。
@ActivityScope
@ContributesAndroidInjector(modules = { /* modules to install into the subcomponent */ })
abstract YourActivity contributeYourAndroidInjector();
4.接下来,让你的Application实现HasAndroidInjector,使用@Inject注解androidInjector()返回的DispatchingAnroidInjector对象
public class YourApplication extends Application implements HasAndroidInjector {
@Inject DispatchingAndroidInjector<Object> dispatchingAndroidInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerYourApplicationComponent.create()
.inject(this);
}
@Override
public AndroidInjector<Object> androidInjector() {
return dispatchingAndroidInjector;
}
}
5.终于,在你的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中,DispatchingAndroidInjector查找你的Activity中的类对应AndroidInjector.Factory(就是YourActivitySubcomponent.Factory),创建AndroidInjector,通过injcect()注入到你的Activity中。
注入Fragment对象
在Fragment中注入对象和在Activity中一样简单,以同样的方式定义你的Subcomponent。
不同于在Activity的onCreat()中注入参数,Fragment是在onAttach()中。
与Activity定义Module不同,你可以选择在任何地方制定Fragment的Module.你可以使你的Fragmet对应的Component是另一个Fragment对应Component的Subcomponent,一个Activity对应的Component,或者是Application对应的Component,这完全去解决你的Fragment需要哪些依赖。在确定好Component的位置之后,使相应的类实现HasAndroidInjector(若果没有准备)。例如,如果你的Fragment需要绑定YourActivitySubcomponent,你的代买将看起来如下:
public class YourActivity extends Activity
implements HasAndroidInjector {
@Inject DispatchingAndroidInjector<Object> androidInjector;
@Override
public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
// ...
}
@Override
public AndroidInjector<Object> androidInjector() {
return androidInjector;
}
}
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.Factory
public interface Factory extends AndroidInjector.Factory<YourFragment> {}
}
@Module(subcomponents = YourFragmentSubcomponent.class)
abstract class YourFragmentModule {
@Binds
@IntoMap
@ClassKey(YourFragment.class)
abstract AndroidInjector.Factory<?>
bindYourFragmentInjectorFactory(YourFragmentSubcomponent.Factory factory);
}
@Subcomponent(modules = { YourFragmentModule.class, ... }
public interface YourActivityOrYourApplicationComponent { ... }
基本框架类型
因为DispatchingAndroidInjector在运行时按类查找相应的AndroidInjector.Faactory,所以一个积累可以实现HasAndroidInjector并调用AndroidInjection.inject().每一个子类需要做的就是绑定一个对应的@Subcomponent,Dagger提供了几个这样的基本类型,例如DaggerActivity和DaggerFragmetn.如果你没有有负责的层级结构,Dagger也提供了一个用于相同目的的DaggerApplication--你需要做的就是继承他并重写applicationInjector(),并返回应该注入到Application中的Component.
还包括一下类型:
注意:DaggerBroadcastReceiver仅应该在AndroidManifest.xml注册BroadcastReceive时使用。使用你自己的代码创建BroadcastReceive时,请改用构造函数注入。
Support libraries
dagger.android.support package中给用户提供Android support libary,并行类型。
TODO(ronshapiro):我们应该开始通过androidx包对此进行拆分
我怎么引用Dagger2
在你的build.gragle中添加一下内容:
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'
}
参考资料
备注:
翻译水平有限,此文有任何可以错误或者需要改进的地方,请在评论区指出,我会修改。