本文未經(jīng)授權(quán)散休,切勿轉(zhuǎn)載
前言
??從業(yè)Android開(kāi)發(fā)快兩年多了次屠,接觸到不同得框架园匹,從最開(kāi)始ButterKnife到后面Kotlin得Kotlin-android-extensions,再到了DataBinding和ViewBinding雳刺,其中印象最深刻不是這類(lèi),也不是像RxJava2這類(lèi)裸违,而是依賴(lài)注入組件諸如Dagger2,再到后來(lái)得Koin煞烫,以及最新的Hilt。那么不如我們自己嘗試看看自己手動(dòng)寫(xiě)能去到什么地步累颂。
前期分析
我大概分析了幾點(diǎn)我們是需要去解決的:</br>一滞详、在基于不進(jìn)行反射下,如何保存好我們預(yù)先初始化的內(nèi)容紊馏,或者我們要初始化的對(duì)象料饥。</br>二、作用域的問(wèn)題朱监,我初始化的Module
到底初始化哪個(gè)作用域的問(wèn)題岸啡,還有一種全局都能用的Module
。</br>
三赫编、當(dāng)我們的Module
包含了存在生命周期的東西巡蘸,如持有LifeOwnwer
的Activity
,或者Fragment
等等類(lèi)似的類(lèi)。
快速開(kāi)始
該項(xiàng)目地址在這里Kinit擂送,基于Kotlin進(jìn)行開(kāi)發(fā)的悦荒,以及reified
,DSL語(yǔ)法
startInit {
enableLog()
single { RoomApi.getDao() }
single { RetroHttp.createApi(Main::class.java) }
}
如果看過(guò)我那篇文章
Android開(kāi)發(fā): 分享如何利用好Kotlin的特點(diǎn)(一)---- 提高開(kāi)發(fā)效率
應(yīng)該有印象開(kāi)篇就講到如何利用Lazy進(jìn)行全局初始化,當(dāng)時(shí)就是這個(gè)項(xiàng)目的雛形嘹吨。如果我們要在注入到ViewModel內(nèi)的對(duì)象呢,需要在Activity使用這個(gè)Module
,由于我們前面分析的第三點(diǎn)搬味,這里使用LifeModule
,進(jìn)行生命周期的監(jiān)聽(tīng)蟀拷,然后把對(duì)象從儲(chǔ)存池內(nèi)移走,如下:</br>
Activity.kt
private val viewModel : MainViewModel by viewModels { ViewModelProvider.AndroidViewModelFactory(application)}
val module = lifeModule {
factory(MainViewModel::class.java.name){ this@MainActivity }
}
startInit {
module(viewModel,module)
}
ViewModel.kt
我們只需要在Activity初始化module
第一個(gè)參數(shù)傳入特征對(duì)象碰纬,然后在需要注入的地方,再次把特征對(duì)象傳入lifeOwnerOrNull
方法中问芬,所以我建議把這個(gè)特征類(lèi)選擇為注入所在那個(gè)類(lèi)悦析。
private val repository by lazy { MainRepository(lifeOwnerOrNull(this))}
MainRepository.kt
class MainRepository(owner: LifecycleOwner?) : BaseRepository(owner)
詳細(xì)可以直接查看我項(xiàng)目的ReadMe,后續(xù)會(huì)補(bǔ)充中文的ReadMe</br>
依賴(lài)方法:App build.gradle
//Core
api("com.github.ShowMeThe.kinit:kinit_core:v0.03")
//Lifecycle-ktx
api("com.github.ShowMeThe.kinit:kinit_lifecycle:v0.03")
還有記得在項(xiàng)目目錄的build.gradle添加
allprojects {
repositories {
........
maven {
setUrl("https://jitpack.io")
}
}
}
核心分析
通過(guò)QuickStart
大概已經(jīng)知道如何使用了,那接下來(lái)就分析主要構(gòu)成部分
限定符
其實(shí)這個(gè)限定符就是一個(gè)ConcurrentHashMap
的KEY
的此衅,控制KEY
來(lái)控制特征對(duì)應(yīng)存儲(chǔ)不同的Module
或者全局初始化對(duì)象
open class Qualifier<D> {
private var key : D? = null
private var typeName:String? = ""
fun setTypeName(typeName: String?){
this.typeName = typeName
}
fun getTypeName() = typeName
fun getKey() = key
fun setKeyName(key:D){
this.key = key
}
inline fun<reified T> Qualifier<*>.makeTypeName(){
setTypeName(T::class.java.name)
}
override fun hashCode(): Int {
return getKey().hashCode() + getTypeName().hashCode()
}
override fun equals(other: Any?): Boolean {
return if(other is Qualifier<*>){
other.key == this.key && other.typeName == this.typeName
}else{
false
}
}
override fun toString(): String {
return "Qualifier[Key:${key},TypeName:${typeName}]"
}
}
模塊
模塊里面存儲(chǔ)的也包含一個(gè)ConcurrentHashMap
强戴,用于存儲(chǔ)不同的初始化的內(nèi)容,通過(guò)唯一的name
炕柔,即ConcurrentHashMap
里面的KEY
fun module(scope:Module.()->Unit):Module{
val moduleBean = Module()
scope.invoke(moduleBean)
return moduleBean
}
open class Module {
var qualifier : Qualifier<*>? = null
set(value) {
field = value
setParentKey(field)
}
private val entry = ConcurrentHashMap<String,Any?>()
fun getEntry() = entry
inline fun factory(name:String,single: ()-> Any){
addSingle(name,single())
}
fun addSingle(name: String,any: Any){
getEntry()[name] = any
}
fun get(name: String):Any?{
return getEntry()[name]
}
open fun setParentKey(qualifier: Qualifier<*>?){
}
fun getKeys() = entry.keys
}
有了上面這兩個(gè)酌泰,限定和作用域的控制,剩下都是存儲(chǔ)的問(wèn)題匕累,以及處理各種提取內(nèi)容
的方法陵刹,以及相應(yīng)的拓展方法,方便取出數(shù)據(jù)欢嘿。
總結(jié)
整體框架使用起來(lái)衰琐,建立在我某一個(gè)項(xiàng)目中也糊,采用注解+反射的方法注入對(duì)象,這個(gè)改善了注入的速度羡宙。目前框架由于我一個(gè)人維護(hù)狸剃,白天要上班,只能晚上寫(xiě)寫(xiě)狗热,能力和時(shí)間都有限钞馁,想法也是有限的,所以歡迎各位Pull Request
或者有問(wèn)題留言Issue
匿刮,我抽空會(huì)回復(fù)僧凰。