前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >放弃dagger?Anrdoi依赖注入框架koin

放弃dagger?Anrdoi依赖注入框架koin

作者头像
六月的雨
发布2020-07-28 15:18:09
1.6K0
发布2020-07-28 15:18:09
举报
文章被收录于专栏:Android开发指南

Koin 是什么

Koin 是为 Kotlin 开发者提供的一个实用型轻量级依赖注入框架,采用纯 Kotlin 语言编写而成,仅使用功能解析,无代理、无代码生成、无反射。

优势

依赖注入好处

  • 增加开发效率、省去重复的简单体力劳动 首先new一个实例的过程是一个重复的简单体力劳动,依赖注入可以把new一个实例的工作做了,因此我们把主要精力集中在关键业务上、同时也能增加开发效率上。
  • 代码更具可读性
  • 省去写单例的方法
  • 解耦 假如不用依赖注入的话,一个类的new代码是非常可能充斥在app的多个类中的,假如该类的构造函数发生变化,那这些涉及到的类都得进行修改。

和dagger相比

  1. 编译生成的代码少
  2. 编译时间少
  3. 上手简单

使用方法

1.添加依赖

代码语言:javascript
复制
// Add Jcenter to your repositories if needed
repositories {
    jcenter()    
}
dependencies {
    // Koin for Android
    compile "org.koin:koin-android:$koin_version"
}

2.比如创建一个HelloRepository来提供一些数据:

代码语言:javascript
复制
interface HelloRepository {
    fun giveHello(): String
}

class HelloRepositoryImpl() : HelloRepository {
    override fun giveHello() = "Hello Koin"
}

3.创建一个presenter类,用来使用这些数据:

代码语言:javascript
复制
class MySimplePresenter(val repo: HelloRepository) {

    fun sayHello() = "${repo.giveHello()} from $this"
}

4.编写Koin模块,使用该module函数声明模块。

代码语言:javascript
复制
val appModule = module {

    // single instance of HelloRepository
    single<HelloRepository> { HelloRepositoryImpl() }

    // Simple Presenter Factory
    factory { MySimplePresenter(get()) }
}

factory每次Activity需要一个实例时都会创建一个新实例。 single 区别在于其提供的实例是单例的 get()这里的功能是直接检索实例(非延迟)

5.启动koin 现在有了一个模块,只需要在Application里调用startKoin()函数:

代码语言:javascript
复制
class MyApplication : Application(){
    override fun onCreate() {
        super.onCreate()
        // Start Koin
        startKoin{
            androidLogger()
            androidContext(this@MyApplication)
            modules(appModule)
        }
    }
}

6.注入依赖 该MySimplePresenter组件将使用HelloRepository实例创建。用by inject()委托注入器注入它:

代码语言:javascript
复制
class MySimpleActivity : AppCompatActivity() {

    // Lazy injected MySimplePresenter
    val firstPresenter: MySimplePresenter by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //...
    }
}

该by inject()功能使我们能够在Android组件运行时(活动,片段,服务…)中检索Koin实例。

原理

内联函数
  • Koin使用了很多的内联函数,它的作用简单来说就是方便进行类型推导,能具体化类型参数。
  • 被inline标记的函数就是内联函数,其原理就是:在编译时期,把调用这个函数的地方用这个函数的方法体进行替换
代码语言:javascript
复制
fun <T> method(lock: Lock, body: () -> T): T {
        lock.lock()
        try {
            return body()
        } finally {
            lock.unlock()
        }
    }


method(lock, {"我是body方法体"})//lock是一个Lock对象

其实上面调用的方法,在编译时期就会把下面的内容替换到调用该方法的地方,这样就会减少方法压栈,出栈,进而减少资源消耗;

代码语言:javascript
复制
        lock.lock()
        try {
            return "我是body方法体"
        } finally {
            lock.unlock()
        }

也就是说inline关键字实际上增加了代码量,但是提升了性能,而且增加的代码量是在编译期执行的,对程序可读性不会造成影响

Reified
  • 由于 Java 中的泛型存在类型擦除的情况,任何在运行时需要知道泛型确切类型信息的操作都没法用了。比如你不能检查一个对象是否为泛型类型 T 的实例,所以需要反射。
  • 而reified,字面意思:具体化,,其实就是具体化泛型。
  • 主要还是有内联函数inline,才使得kotlin能够直接通过泛型就能拿到泛型的类型,只有内联函数的类型参数可以具体化。

例子 定义实现一个扩展函数启动 Activity,一般都需要传 Class 参数:

代码语言:javascript
复制
// Function
private fun <T : Activity> Activity.startActivity(context: Context, clazz: Class<T>) {
    startActivity(Intent(context, clazz))
}

// Caller
startActivity(context, NewActivity::class.java)

使用 reified,通过添加类型传递简化泛型参数

代码语言:javascript
复制
// Function
inline fun <reified T : Activity> Activity.startActivity(context: Context) {
    startActivity(Intent(context, T::class.java))
}

// Caller
startActivity<NewActivity>(context)
注入流程
  • 内联函数支持具体化的类型参数,使用 reified 修饰符来限定类型参数,可以在函数内部访问它,由于函数是内联的,所以不需要反射。
  • koin里有一个全局的容器,提供了应用所有所需实例的构造方式,那么当我们需要新建实例的时候,就可以直接从这个容器里面获取到它的构造方式然后拿到所需的依赖,构造出所需的实例就可以了。
代码语言:javascript
复制
startKoin(this, appModule, logger = AndroidLogger(showDebug = BuildConfig.DEBUG))
  • Koin提供一个全局容器,将所有的依赖构造方式转换成 BeanDefinition 进行注册,这是一个HashSet,名字是 definitions。
UhlZpF.png
UhlZpF.png

BeanDefinition

UhlM01.png
UhlM01.png
  • name以及primaryType,这两个是get()关键字依赖检索所需的key。
  • definition: Definition,它的值代表了其构造方式来源于那个module,对应前文的appModule,通过它可以反向推导该实例需要哪些依赖。
代码语言:javascript
复制
  override fun <T> get(parameters: ParameterDefinition): Instance<T> {
        val needCreation = instance == null
        if (needCreation) {
            instance = create(parameters)
        }
        return Instance(instance as T, needCreation)
    }
    
    
        fun <T> create(parameters: ParameterDefinition): T {
        try {
            val parameterList = parameters()
            val instance = bean.definition.invoke(parameterList) as Any
            instance as T  //创建参数的实例
            return instance
        } catch (e: Throwable) {
              // ....
        }
    }

总结

  • 假如需要一个 MainViewModel 的实例,那么通过clazz为Class的key在definitions中进行查找。
  • 查到有一个 MainViewModel 的 BeanDefinition,通过注册过的 definition: Definition找到其构造方式的位置(module)。
  • 当通过 MainViewModel(get() 的构造方式去构造 MainViewModel 实例的时候,发现又有一个get(),然后就是再重复前面的逻辑,一直到生成ViewModel实例为止。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-07-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Koin 是什么
  • 优势
  • 使用方法
  • 原理
    • 内联函数
      • Reified
        • 注入流程
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档