使用Koin来完成Kotlin的依赖注入

5,065 阅读3分钟

在之前使用MVP的同学,如果你使用过依赖注入框架Dagger的话,你就会发现,它是多么的难用,这里对Dagger的使用就不做介绍了。我们来一起学习在kotlin上的新的依赖注入框架koin

本文使用kotlin + Jetpack

按照谷歌的建议,一个APP,应该包含UI层,ViewModel层,Repository层,这里简单介绍一下,UI持有ViewModel的引用,ViewModel持有Repository的引用,Repository持有数据库Dao层和网络Network层

使用Koin

引入依赖:

def retrofit_version = '2.6.0'
def moshiVersion = '1.8.0'
def koin_version = "2.0.1"
def viewmodel_ktx = '2.2.0-alpha03'
def kotlinCoroutineVersion = "1.0.1"

// retrofit以及moshi转换,将响应的结果解析成对象
implementation "com.squareup.retrofit2:retrofit:${retrofit_version}"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"

// moshi 解析JSON
implementation "com.squareup.moshi:moshi-kotlin:$moshiVersion"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"

// koin 依赖注入
implementation "org.koin:koin-android:$koin_version"
implementation "org.koin:koin-android-viewmodel:$koin_version"

// 可使用协程并可以观察生命周期的ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$viewmodel_ktx"

// 协程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutineVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutineVersion"
    
    

看完所需要的依赖,有的同学可能会问,为什么没有Retrofit的adapter,像implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'等,这里想介绍的是协程,Retrofit的2.6.0版本已经开始支持suspend函数了。

Koin的使用需要我们在Application里面进行注册:

override fun onCreate() {
        super.onCreate()
        startKoin{
            androidLogger()
            androidContext(this@BaseApplication)
            modules(listOf(repoModule, viewmodelModule, networkModule))
        }
    }

在Application里面,注册了三个module,分别对应的是我们的仓库层,ViewModel层,网络层

新建文件夹di,我们将三个module写在这里

// NetworkModule.kt
val api = ApiRepo.api
val networkModule = module {
    single {
        api
    }
}

// RepoModule.kt
val repoModule = module {
    single {
        MainRepo(get())
    }
}

// ViewModelModule.kt
val viewmodelModule = module {
    viewModel {
        MainViewModel(get())
    }
}
// ApiRepo.kt
object ApiRepo {
    private val okHttpClient = OkHttpClient.Builder()
        .build()

    private val retrofit = Retrofit.Builder()
        .client(okHttpClient)
        .baseUrl("your baseurl")
        .addConverterFactory(MoshiConverterFactory.create())
        .build()

    val api : Api = retrofit.create(Api::class.java)
}

// Api.kt
interface Api {
    @GET("")
    suspend fun getCountryData(): BaseModel<List<CountryModel>>
}

上面的代码很简单,我们每次新建的ViewModel,Repository之后,在viewmodelModule.kt和RepoModule.kt分别进行声明即可,我们看到,声明ViewModel的时候使用的是MainViewModel(get()),这里多了个get(),为什么?因为我们的ViewModel层是要持有仓库层的引用的,使用get()的话,就会去在所有的注入的类里面去寻找我们需要的,简单吧。

我们看到在API里,我们在调用接口的时候使用了suspend关键字,返回的结果也不再是一个Call了,我们在使用的时候只需要开一个协程,即可拿到我们需要的数据,然后使用LiveData就能将数据发送到我们的UI上

下面我们看具体的代码:

// MainRepo.kt
class MainRepo(private val api:Api) {
    suspend fun getCountryData() : BaseModel<List<CountryModel>> = api.getCountryData()
}

// MainViewModel.kt
class MainViewModel(private val repo: MainRepo) : ViewModel() {

    var countryLiveData = MutableLiveData<BaseModel<List<CountryModel>>()
    fun getCountryData() {
    // 在Activity销毁的时候,这里会自动关闭
        viewModelScope.launch {
            countryLiveData.value = fetch(repo.getCountryData())
        }
    }
}


class MainActivity : AppCompatActivity() {
    
    // 只需要通过koin的by viewModel就可以拿到我们注册的MainViewModel了
    private val mainViewModel by viewModel<MainViewModel>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        mainViewModel.getCountryData()
        mainViewModel.countryLiveData.observe(this, Observer {
            // do something
        })
        
    }
}

总结

  • 使用了koin,在业务复杂的时候就方便了许多,再也不用通过构造函数传来传去了

  • Retrofit的2.6.0版本已经默认支持协程了,可能有些同学会觉得,就使用一个suspend的话,有些异常该怎么处理,其实我们在ViewModel层,在调用仓库层的方法的时候,可以使用一个try-catch即可去处理异常了,或者来一个BaseViewModel,统一处理

  • 使用LiveData + ViewModel + coroutines,在请求网络的时候就不用担心切换线程啦,页面销毁时如何取消请求啦等问题了,当然,ViewModel要使用上面gradle里面依赖的ViewModel