本文未经授权,切勿转载
前言
从业Android开发快两年多了,接触到不同得框架,从最开始ButterKnife
到后面Kotlin
得Kotlin-android-extensions
,再到了DataBinding
和ViewBinding
,其中印象最深刻不是这类,也不是像RxJava2
这类,而是依赖注入组件诸如Dagger2
,再到后来得Koin
,以及最新的Hilt
。那么不如我们自己尝试看看自己手动写能否写出类似框架。
前期分析
我大概分析了几点我们是需要去解决的:
一、在基于不进行反射下,如何保存好我们预先初始化的内容,或者我们要初始化的对象。
二、作用域的问题,我初始化的Module
到底初始化哪个作用域的问题,还有一种全局都能用的Module
。
三、当我们的Module
包含了存在生命周期的东西,如持有LifeOwnwer
的Activity
,或者Fragment
等等类似的类。
开始第一行代码
前言
该项目地址在这里Kinject
框架基于Kotlin进行开发的,运用到reified
,DSL
等语法和方法。
如果看过我那篇文章Android开发: 分享如何利用好Kotlin的特点(一)---- 提高开发效率
应该有印象开篇就讲到如何利用Lazy进行全局初始化,当时就是这个项目的雏形。如果我们要在注入到ViewModel内的对象呢,需要在Activity使用这个Module
,由我们前面分析的第三点,需要对生命周期进行监听。
分析准备工作
那我们第一步应该是先构建一个池子装着这些Module
,总所周知,这个池子我们常用的HashMap
,ArraryMap
之类的KEY-VALUE
池,我这里使用ConcurrentHashMap
,为了并发的兼容。
那我们先定义好这个KEY
,首先这个KEY
,需要有什么,很简答唯一的内容,确保能到唯一的内容下,找到唯一答案。所以就有个以下内容:
open class Qualifier<D> {
private var key : D? = null
fun getKey() = key
fun setKeyName(key:D){
this.key = key
}
override fun hashCode(): Int {
return 31 * getKey().hashCode()
}
override fun equals(other: Any?): Boolean {
return if(other is Qualifier<*>){
other.key == this.key
}else{
false
}
}
override fun toString(): String {
return "Qualifier[Key:${key}]"
}
}
然后我们主要是利用字符串作为判别,那么就变成了:
class StringQualifier : Qualifier<String>()
KEY
完成了,那么接下来要决定VALUE
的内容,很明显VALUE
就是我们的Module
,
其实Moduel
也很简单,就是存对象。
private val entrySingle by lazy { ConcurrentHashMap<String, Any>() }
private val entryFactory by lazy { ConcurrentHashMap<String, () -> Any?>() }
利用两个ConcurrentHashMap
,第一个用来存单例或者,初始化一次的内容,第二个存方法,然后每次调用都会重新执行方法,都是新的对象如下:
第二步,对生命周期的处理
其实到了这一步基本上已经完成了简易框架的大部分内容了,接下来只是对含有生命周期的区域进行管理,我们只要继承好LifecycleObserver
,在适当的生命周期中移走我们存的内容即可。
实际运用
这个实际应用在我另一个项目MaterialWanAndroid客户端上进行应用,核心内容是
initScope {
module(viewModel, lifeModule {
scopeLifeOwner(viewModel)
})
}
在Activity
里对ViewModel
对象,注入LifeOwner
,然后我们的仓库XXXReposiory
只需要调用getLifeOwner
方法,如下:
if(owner == null){
owner = getLifeOwner(viewModel)
owner?.lifecycle?.addObserver(this)
}
其中这个viewModel
,就是我们在Activity中初始化的viewModel
。
结尾
其实这样手写的简易注入框架还是属于比较简陋的,但是也确实满足了大部分的开发
诸如单例的Retorfit
的Api
initScope {
single { Retorfit.create(Main::class.java) }
}
然后在仓库中我们只需要,这样初始化即可使用,数据库也是类似原理
private val api : Main by single()