Kotlin Dagger2
首先:进行运行环境的配置
step1:
导入build.grable闭包配置
在需要使用Dagger2模块的build.grable文件内添加(运行环境和jar包)
implementation "com.google.dagger:dagger:2.20"
kapt "com.google.dagger:dagger-compiler:2.20"
step2:
在使用模块的build.gradle 内添加
apply plugin: 'kotlin-kapt'
主要内容
依赖注入:
在当前类运行时当前对象与某个对象发生依赖关系,把这种依赖在一个合适的时候注入“运行时”,也就是依赖注入,在没调用时不与其他对象发生依赖关系,在运行调用时就与其他对象发生依赖关系。
@inject 和 @component : 注解 和 组件
@Module 和 @Provides :在module 内有一个 Provides
@Scope 和 @Singleton :作用域(使用范围) 和 使用单例模式
@Qualifier 和 @Named :限定符
使用Dagger2 依赖注入
@Inject:
也就是注入想要使用的对象实例,注意这里注入的时实例,所以使用注入不需要实例化对象。
使用 @Inject 注解,可以不需要实例化带有依赖注入注解 @Inject 的对象。使用时需要加入lateinit关键字,这样可以在不调用时不占用多余的内存空间。
@Inject时对应出现的,被依赖对象要使用@Inject 依赖对象也要使用@Inject。
@Component:注入器,连接目标类和依赖类的桥梁
@Component注解的必须时接口或者抽象类。Component依赖关系是通过dependencies属性添加。
App必须有一个Component用来管理全局实例
简单说就是,可以通过Component访问到Module中提供的依赖注入对象。假设,如果有两个Module,AModule、BModule,如果Component只注册了AModule,而没有注册BModule,那么BModule中提供的对象,无法进行依赖注入!
@Module:
@Module用来解决的问题
1.使用第三方库的时候,如果第三方库没有使用dagger2 的@Inject并且第三方库无法修改其中的内容,那么也就无法使用@Inject注解了
2.接口不能实例化,只能通过实现类实例化。
@Module的使用
Module是一个简单工厂,是用来床创建类实例的方法,比如第三方库中的类无法修改,但是还是要使用@Inject注解的化,那么就可以把想要注解的对象的实例化过程提取到Module工厂里,这样就不怕无法使用@Inject标记实例化而无法使用@Inject注解
@Component 可以通过modules属性加入多个module,这样就可以得到多个实例化工厂中的对象。
@provides:
@provides是在Module中使用的,Module是一个提供具体实例化对象的工厂,那么@provides就是Module中用来标注创建实例的方法。
实例化流程
Component搜索@Inject注解的属性
Component查找Module中以@Provides注解的对应方法,创建实例
Inject 和 Module 维度
(一种是直接使用@Inject标记被实现对象和实现对象来实例化)
(一种是使用Module在Module里使用@Provides注解来实例化具体的对象)
注意:
如果@Inject 和 Module 两种创建方式都存在的化,那么Module的优先级会高@Inject
比如先去找Module中有没有实现这个对象,如果没有的话再去@Inject中找对象
具体使用:
第一种:直接使用@Inject注解标注需要实现的属性对象
第一步:在引用属性加上 @Inject注解
如:这是属性 mPresenter:T 加上@Inject注解
open class BaseMvpActivity<T: BasePresenter<out BaseView>> :BaseActivity(),BaseView {
override fun showLoading() {
}
override fun hideLoading() {
}
override fun onError() {
}
// 创建一个BasePresenter的引用
@Inject
lateinit var mPresenter: T
}
第二步:被@Inject标注的具体类型对象需要在构造函数加上@Inject标注
因为类的引用属性有被@Inject标注,既然标注了就需要实现当前引用属性的具体类,使用就需要在具体类的构造方法加上@Inject标注,如果当前的引用用来实例化多个类,那么那几个有被实现的类都需要在构造方法前加上@Inject (如:@Inject constructor())
如:RegisterPresenter是一个具体的实现类,它实现了@Inject标注的引用属性的类
class RegisterPresenter @Inject constructor(): BasePresenter<RegisterView>(){
// 这里由于使用了Dagger2 的@Inject注解所以当前类可以使用@Inject注解引用的实例对象
@Inject
lateinit var userService: UserService
fun register(mobile: String, verifyCode: String, pwd: String){
/*
业务逻辑
如:请求网络,做注册,将最后的结果返回给Activity
*/
// val userService = UserServiceImpl()
// register 就是一个被被监听者
userService.register(mobile,verifyCode,pwd)
.execute(object : BaseResoureSubscriber<Boolean>(){
override fun onNext(t: Boolean) {
mView.onRegisterResult(t)
}
})
}
fun login(mobile: String,pwd: String){
/*
业务逻辑
如:请求网络,做注册,将最后的结果返回给Activity
*/
val userService = UserServiceImpl()
// register 就是一个被被监听者
userService.register(mobile,"",pwd)
.execute(object : BaseResoureSubscriber<Boolean>(){
override fun onNext(t: Boolean) {
mView.onRegisterResult(t)
}
})
}
}
第二种,使用Module来实现接口的实现类
第一步,实现Module工厂类
@Module
class UserModule {
/**
* @param service 通过@Provides 标注之后,Dagger2就会使用递归的形式
* 去查找当前被标注方法的参数列表的具体类是否有被@Inject标注
* 如果参数列表中的具体实现类有被@Inject标注,
* 那么就将当前参数名称的属性实例化使属性带有具体的实现,
* 也就是实例化的对象。
*
* @return 它的返回类型是当前参数列表中具体类实现的接口类型
*/
@Provides
fun providesUserService(service: UserServiceImpl): UserService{
return service
}
}
第二步,使用@Inject标注Module工厂中想要使用@Provides实现的接口实现类
如:UsrServiceImpl是一个实现了UsrService接口的类
class UserServiceImpl @Inject constructor() : UserService {
// 在具体的类 UserRepository的构造方法加上标注 @Inject,
// 那么这里只要使用@Inject标注一个延迟实例化的属性,
// 只要当前被@Inject标注的属性有被调用到,那么@Inject
// 就会自动找到当前标注的属性的具体类,通过具体类去实现当前的
// 属性对象。
@Inject
lateinit var repository: UserRepository
override fun register(mobile: String, verifyCode: String, pwd: String): Flowable<Boolean> {
// val repository = UserRepository()
return Flowable.create({ it.onNext(true) }, BackpressureStrategy.BUFFER)
}
}
第三步,使用@Inject lateinit 延迟加载在调用时通过Module工厂自动实例化类
@Inject
lateinit var userService: UserService
第四步,使用Component标注把Module中得到的实例注入到具体的类中,这样被注入的实例才可以使用Module中的实例。
// 要注入的Module
@Component(modules = arrayOf(UserModule::class))
interface UserComponent {
// component 需要注入的地方,也就是主调用的地方
fun inject(activity:RegisterActivity)
}
第五步,使用内部创建的Compoent对象注入依赖,具体看以下代码。
与当前对象依赖的有Register
class RegisterActivity : BaseMvpActivity<RegisterPresenter>(),RegisterView {
override fun onRegisterResult(result: Boolean) {
toast("注册成功").show()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_register)
// mPresenter = RegisterPresenter()
// mPresenter.mView = this
initInjection()
mRegisterBtn.setOnClickListener {
mPresenter.register(mMobileEt.text.toString(),mVerifyCodeEt.text.toString(),mPwdEt.text.toString())
}
}
private fun initInjection() {
/*
注入Module,通过builder得到Builder的引用,userModule用来检查UserModule时否为空,
通过build得到UserModule的对象,最后通过inject将当前位置的对象注入到Component,创建于当前对象有依赖的所有对象。
比如以上使用mPresenter.register调用了UserService的实现方法,就等于register
方法得到了UserService接口的注入。
*/
DaggerUserComponent.builder().userModule(UserModule()).build().inject(this)
mPresenter.mView = this
}
}
@Scope
@Conpe主要用于Component的组织方式,(也就是用来标注Component的作用域级别)
管理COmponent和Module之间的匹配关系
提高可读性,见名知意
@Singleton
@Scope是一个接口,@Singleton是实现@Scope的一个子类
@Singleton 是@Scope的一种默认实现
@Singleton并没有实现单例的能力
ApplicationComponent单例是由代码控制实现的
@Scope的使用
以Component组织方式自定义Scope
自定义Scope与Dagger2的配置流程总结:
说明:@Singleton 的具体作用是在创建 Component进行module注入的时候,
如果@Component是被@Singleton标注的,
// 比如,AppComponent可以注入的是由被@Singleton标注的module工厂
@Singleton
// @component是被@Singleton标注的,所以只能注入module中被@Singleton标注的工厂方法的实例
@Component(modules = arrayOf(AppModule::class))
那么@Component(module = arrayof(module:: class)),
这里的module工厂的工厂方法也必须是被@Singleton标注的
@Module
class AppModule(private val context: BaseApplication) {
// @Singleton 标注的后就可以使后台知道Component可以传入的是哪个module
@Singleton
// module 创建所有与BaseApplication由所依赖并且由加@Inject标注的实例对象
@Provides
fun providesActivity(): Context {
return context
}
}
自定义Scope 如果想要由多个@Singleton 来标注多个作用域,也就是多个模块那么就可以自己自定义一个@Singleton
一个ActivityScope
@Scope
@Documented
@Retention(RUNTIME)
annotation class ActivityScope
用自定义@Scope来标注module工厂方法
@Module
class ActivityModule(private val activity: Activity) {
// module 创建所有与BaseApplication由所依赖并且由加@Inject标注的实例对象
@Provides
fun providesContext(): Activity {
return activity
}
}
注意:@Singleton在标注module时,必须标注在工厂方法,不可标注在整个module类,并且如果@Singleton标注的时某个高层父类,那么其他module中的工厂方法如果有实现了这个高层父类,就不可用其他标注标注这个工厂方法
总结:dagger2 在查找创建对象的过程
step1: 创建一个module
找module中是否有创建该对象的工厂(这也就是为什么module用来处理第三方框架不能使用dagger2 的原因了,因为它可以让我们自己添加创建对象的工厂而不是
通过@Inject标注具体对象的构造方法后在其他地方使用@Injec标注有被@Inject标注的实例就可以使用dagger2创建实例而不用我们自己去创建实例)
step2:创建一个component注入
component注入是用于那些可以方便修改本地代码的场景,而第三方无法修改代码就只能用module
compenent的使用是通过将具体的类文件注入到使用的地方,那些有被@inject标的对象或者module持有对象工厂的对象都将得到创建,在使用这些对象的时候将不需要显示的创建对象。
step3:在被注入的地方或者想要使用注入对象的地方进行操作
被注入的地方使用代码
DaggerUserComponent.builder().userModule(UserModule()).build().inject(this)
;这段代码用来创建传入当前实例到module进行创建实例
;具体的执行流程
dagger2默认生成 DaggerUserCompent这里的UserCompent就是使用@Component标注进行注入对象的文件模板。
再用builder() 来得到Builder()的引用,Builder()内就存在一个userModule用来检查传入的UserModule() 对象是否为空,不为空则使用build()来得到DaggerUserComponent() 对象, 使用对象中的inject()接口方法传入当前对象到 UserComponent进行注入对象
最后,在当前类内部就可以使用module工厂内的对象
@Qualifier
注解迷失:同一个接口有多个实现鳄类,编译报错,分不清使用哪一个实现类
使用限定符分
@Named
Qualifier的一种实现方式
以名称区分使用的那种注解实现
自定义Qualifier
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
annotation.class.ActivityQualifier
使用:比如一个module里有两个返回同样接口类型的工厂方法,那么在使用@Inject注解调用实现对象的时候,后台机制将不知道如何选择调用哪个工厂方法来实例化类,
这里使用了@Named() 括号内标注的就相当于一个用来区分工厂方法的标识符,第一个工厂方法在括号内使用的表示名不和第二个标识符的一样,在使用@Inject来标注本地实现的引用对象时,要想知道具体时实例化哪一个对象,就得加上@Named(),括号内的区分字符串来区分实例化的对象
定义
/**
- @创建者 xia
- @创建时间 2018/12/27 21:11
- @描述 一个用于创建接口实现类实例对象的工厂
/
@Module
class UserModule {
/*- @param service 通过@Provides 标注之后,Dagger2就会使用递归的形式
- 去查找当前被标注方法的参数列表的具体类是否有被@Inject标注
- 如果参数列表中的具体实现类有被@Inject标注,
- 那么就将当前参数名称的属性实例化使属性带有具体的实现,
- 也就是实例化的对象。
- @return 它的返回类型是当前参数列表中具体类实现的接口类型
*/
@Named("service")
@Provides
fun providesUserService(service: UserServiceImpl): UserService{
return service
}
@Named("service2")
@Provides
fun providesUserService2(service: UserServiceImpl2): UserService{
return service
}
}
使用
class RegisterPresenter @Inject constructor(): BasePresenter<RegisterView>(){
/*
使用@Inject标注属性让Module工厂来实现具体的接口实现类,
因为接口中有使用@Provider标注实现了 UserService接口的类
/
@Inject
// 域变量
@field:[Named("service")]
lateinit var userService: UserService
@Inject
@field:[Named("service2")]
lateinit var userService2: UserService
fun register(mobile: String, verifyCode: String, pwd: String){
/
业务逻辑
如:请求网络,做注册,将最后的结果返回给Activity
*/
// val userService = UserServiceImpl()
// register 就是一个被被监听者
userService.register(mobile,verifyCode,pwd)
.execute(object : BaseResoureSubscriber<Boolean>(){
override fun onNext(t: Boolean) {
mView.onRegisterResult(t)
}
})
}
fun register2(mobile: String, verifyCode: String, pwd: String){
/*
业务逻辑
如:请求网络,做注册,将最后的结果返回给Activity
*/
// val userService = UserServiceImpl()
// register 就是一个被被监听者
userService2.register(mobile,verifyCode,pwd)
.execute(object : BaseResoureSubscriber<Boolean>(){
override fun onNext(t: Boolean) {
mView.onRegisterResult(t)
}
})
}
fun login(mobile: String,pwd: String){
/*
业务逻辑
如:请求网络,做注册,将最后的结果返回给Activity
*/
val userService = UserServiceImpl()
// register 就是一个被被监听者
userService.register(mobile,"",pwd)
.execute(object : BaseResoureSubscriber<Boolean>(){
override fun onNext(t: Boolean) {
mView.onRegisterResult(t)
}
})
}
}