Android:手写一个依赖注入框架

1,563 阅读3分钟

本文未经授权,切勿转载

前言

  从业Android开发快两年多了,接触到不同得框架,从最开始ButterKnife到后面KotlinKotlin-android-extensions,再到了DataBindingViewBinding,其中印象最深刻不是这类,也不是像RxJava2这类,而是依赖注入组件诸如Dagger2,再到后来得Koin,以及最新的Hilt。那么不如我们自己尝试看看自己手动写能否写出类似框架。

前期分析

我大概分析了几点我们是需要去解决的:
一、在基于不进行反射下,如何保存好我们预先初始化的内容,或者我们要初始化的对象。
二、作用域的问题,我初始化的Module到底初始化哪个作用域的问题,还有一种全局都能用的Module
三、当我们的Module包含了存在生命周期的东西,如持有LifeOwnwerActivity,或者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,第一个用来存单例或者,初始化一次的内容,第二个存方法,然后每次调用都会重新执行方法,都是新的对象如下:

20210817114710.png

第二步,对生命周期的处理

其实到了这一步基本上已经完成了简易框架的大部分内容了,接下来只是对含有生命周期的区域进行管理,我们只要继承好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

结尾

其实这样手写的简易注入框架还是属于比较简陋的,但是也确实满足了大部分的开发 诸如单例的RetorfitApi

initScope {
    single { Retorfit.create(Main::class.java) }
}

然后在仓库中我们只需要,这样初始化即可使用,数据库也是类似原理

private val api : Main by single()