Dagger2是一个IOC框架,通常用于Android平台,第一次接触的朋友,必定会被搞得晕头转向。它延续了Java平台Spring框架代码碎片化,注解满天飞的传统。尝试将各处代码片断串联起来,理清思绪,真不是件容易的事。更不用说还有各版本细微的差异。
与Spring不一样的是,Spring是经过反射建立对象的,而Dagger2是[经过apt插件]在编译期间生成代码,这些生成的代码负责依赖对象建立。html
本文旨在以简单通俗易懂的方式说明如何使用Dagger2,对其背后设计不作深刻探讨。人生苦短,码农更甚,先知其然等有空时再知其因此然,不失为撸App的较好实践。java
正式开始前,先给像笔者这样的小白定义几个概念,方便下文理解:android
首先,添加依赖库。git
implementation "com.google.dagger:dagger:2.27" // kapt是服务于Kotlin的Annotation Processing Tool,用于编译时处理注解 kapt "com.google.dagger:dagger-compiler:2.27"
通常来讲,IOC会根据规则在运行时自动帮咱们生成依赖对象实例。Dagger2提供了两种声明依赖对象的方式:github
@Inject
修饰。@Module
修饰的类中所定义的有@Provides
修饰的方法提供(可用于依赖对象是第三方库中的对象)。// 方式一(注意此处hen也要是依赖对象,不然将为null或者直接报错) class Egg @Inject constructor(private val hen: Hen) // 方式二 @Module class HenModule { @Singleton @Provides fun provideHen() = Hen() }
你们注意@Singleton
注解(javax.inject
中定义),它表示该依赖对象的做用域或者说生命周期,Dagger2中可经过@Scope
定义。@Singleton是Dagger2默认支持的scope,表示依赖对象是单例。须要注意的是,一般咱们将单例保存在一个静态域中,这样的单例每每要等到虚拟机关闭时候,所占用的资源才释放,可是,Dagger经过Singleton建立出来的单例并不保持在静态域上,而是保留在一样标注了@Singleton的Component实例中(依赖对象容器,接下来会讲到)。其实对于任意scope,只要依赖对象和Component标注的是相同scope,那么该依赖对象在相应的Component中就是一个局部单例,仅会调用一次工厂类生成对象实例。通常来讲咱们只要使用默认的@Singleton便可,不必自定义,自定义Scope经常使用于业务或逻辑的划分。
若是不想依赖对象与Component绑定,则可使用@Reusable做用域。app
上面说的Component是@Component
注解修饰的接口,Dagger2会先寻找它,以此为入口获得全部依赖对象。该接口中可定义相似void inject(Target target)
的方法。显然,@Component注解的接口就是注入者,它将依赖对象和目标对象串联了起来。框架
@Singleton @Component(modules = [HenModule::class]) //依赖对象 interface MyAppComponent { fun inject(activity: MainActivity); //目标对象 }
最后咱们就能够在目标对象中愉快地使用依赖对象了。ide
class MainActivity : AppCompatActivity() { @Inject lateinit var hen: Hen override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) DaggerMyAppComponent.builder().build().inject(this); // 关键 } }
如上,对于Activity/Fragment来讲,它们的实例化是系统完成的,所以咱们只能在它们使用以前的某个环节好比onCreate回调方法内手动将其自身依附到Dagger2中,这产生了至少一个问题:这种方式破坏了依赖注入的核心准则:一个类不该该知道它是如何被注入的。为了解决这个问题,Dagger 2.10版本引入的dagger-android,它是一个专为Android设计的除了Dagger主模块以外的全新模块。函数
首先,新增两个依赖库。工具
implementation "com.google.dagger:dagger-android:2.27" kapt "com.google.dagger:dagger-android-processor:2.27"
针对每一个目标类编写对应的子容器代码:
@Subcomponent(modules = [HenModule::class]) interface MainActivitySubcomponent : AndroidInjector<MainActivity?> { @Subcomponent.Builder abstract class Builder : AndroidInjector.Factory<MainActivity?> }
再针对每一个目标类编写对应的依赖对象代码:
@Module(subcomponents = MainActivitySubcomponent::class) abstract class MainActivityModule { @Binds @IntoMap @ActivityKey(MainActivity::class) abstract fun bindMainActivityInjectorFactory(builder: MainActivitySubcomponent.Builder?): AndroidInjector.Factory<out Activity?>? }
修改以前的MyAppComponent代码:
@Singleton @Component(modules = [AndroidInjectionModule::class,MainActivityModule::class]) // 关键,引入AndroidInjectionModule interface MyAppComponent { fun inject(app: MyApplication); //注入到Application }
Application
须要继承HasActivityInjector
,实现inject方法,返回DispatchingAndroidInjector
对象。
class MyApplication : Application(), HasActivityInjector { // 由dagger.android自动注入 @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity> override fun androidInjector() = dispatchingAndroidInjector override fun onCreate() { super.onCreate() DaggerMyAppComponent.builder().build().inject(this) } }
建立一个BaseActivity,在super.onCreate以前调用AndroidInjection.inject(this)
,这样以后的Activity就只须要继承它,就可使用各自的依赖对象了。
open class BaseActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) super.onCreate(savedInstanceState) } }
class MainActivity : BaseActivity() { @Inject lateinit var hen: Hen override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //DaggerMyAppComponent.builder().build().inject(this); // 不须要了 } }
可见,为了解决前述问题,引入了更多的代码,复杂度也提升了,这是否有必要,值得商榷。好在Dagger2提供了@ContributesAndroidInjector
注解解决了这个问题。
@Subcomponent interface ActivityComponet: AndroidInjector<BaseActivity>{ @Subcomponent.Builder abstract class Builder: AndroidInjector.Builder<BaseActivity>() }
@Module(subcomponents = [ActivityComponet::class]) abstract class ActivityModule{ @ContributesAndroidInjector abstract fun mainActivityInjector(): MainActivity }
@Singleton @Component(modules = [AndroidInjectionModule::class,ActivityModule::class]) // MainActivityModule改成ActivityModule interface MyAppComponent { fun inject(app: MyApplication); }
大功告成!咱们不再须要重复地建立XXXActivityModule和XXXActivitySubcomponent类了。
参看Dagger & Android
@BindsInstance
:编译后会在Component的Builder类中生成修饰的方法里面的参数对应的成员变量,so该变量对应的对象可在与该Component相关的Module中经过@Inject注入,可看做是该Component范围内的全局单例,相似于上述的@Scope的做用。
Dagger 2.22 起引入了 @Component.Factory
, 能够取代@Component.Builder
的使用,Factory在许多场景的上的使用相对于Builder会更简单。
Dagger 2.23新增了一个HasAndroidInjector
接口,用于替代HasActivityInjector, HasServiceInjector, HasBroadcastReceiverInjector, HasSupportFragmentInjector
四个接口,让Application中的代码更简洁,目前仍是beta版。通常若是咱们只须要HasActivityInjector的话那也无所谓了。参看Reducing Boilerplate with the new HasAndroidInjector in Dagger 2.23
了解一下javax.inject
,这个是 Java EE 6 规范 JSR 330 -- Dependency Injection for Java 中的东西, Spring、Guice兼容该规范。
Assisted Injection
:彷佛是Guice引入的一个概念?——
Sometimes a class gets some of its constructor parameters from the Injector and others from the caller. 对于这种状况,咱们常封装一个工厂类,该类内部提供了注入类型的实例化,对外暴露一个生产方法,该方法只接收须要外部传入的参数。若是以为手写这种工厂类太过麻烦或工做量太大,那么可使用AssistedInject
自动生成。参看AssistedInject,Assisted Injection for JSR 330
抛弃dagger-android
:虽然最终改进以后,代码变得清晰不少,但内在逻辑反而更加复杂了。这种[理解门槛较高的]复杂度就像一颗定时炸弹,让人夜不能寐。当我使用到ViewModel
以后发现,也能够不引入dagger-android,而是将全部依赖注入到ViewModel中,再由ViewModel暴露给系统组件。然而因为框架所限,其实否则——ViewModel是由Android框架自己维护的,固然框架也给咱们留了一个自定义provider viewmodel的口子,就是ViewModelProvider.Factory
——这又是一项颇费脑力的工程,参看How to Inject ViewModel using Dagger 2。这步完成之后,咱们再将ViewModelProvider.Factory实例注入到Application中,变为一个全局工厂对象,Activity/Fragment直接拿来用便可,不再须要与依赖注入有任何瓜葛,天然也不须要dagger-android了。也有大神跟我想到一块,参看当Dagger2撞上ViewModel
Dagger2从入门到放弃再到恍然大悟
Dagger2 @Component 和@SubComponent 区别解惑
学习Dagger2笔记
dagger.android(Dagger2中的AndroidInjector)使用解析
Dagger2 中的 Binds、IntoSet、IntoMap
dagger android 学习(一):dagger基础使用
Dagger2在Android平台上的新魔法(做者了解了dagger-android背后的原理后弃用)