在这篇文章中,我们将使用Koin
来委派我们应用程序的依赖性,例如Raterofit,API服务,重新启动和ViewModels,并在我们的项目中使用接口和用例实现干净的体系结构。
干净的体系结构
a 干净的体系结构(清洁体系结构)是一个名称,旨在专注于应用程序领域的软件开发体系结构;作为驱动程序,框架和磅数仅细节。目的是责任原则,将利益与每项义务分开,并维护业务规则,而无需了解外界的任何细节;因此,可以在不依赖任何外部元素的情况下对其进行测试。
总而言之,我们的系统将分为反式层:
- 演示文稿(Android Mother):负责应用程序接口和从域接收的数据显示。
- 域(Kotlin Mother):负责该项目特定领域的实体和规则。这个母亲应该完全独立于平台 android。
- 基础架构(Android母亲):负责数据库,互联网访问和应用程序的其他详细信息。
在演示层中我们具有 Active 和片段,其中必须没有羊毛不是UI宽的。
另一方面,在域中层层,座位的接口和用例,这是我们的业务,并服务作为演示和基础架构之间的桥梁。
最后,在基础架构层中是我们应用程序的必要数据(在我们的情况下称为API重置),可以从储存库中访问>域。
层什么变化?好吧,我们正在做这样的事情,但是我们的存储库并未在合同(接口)和实施中分开,我们甚至没有使用案例 em>访问 repositors em>的实施。并将数据发送到演示。
要团结所有这些转移并使体系结构起作用,让我们使用Koin
框架应用依赖性反转原理(逆转原理), d 实心。该原则说,高潜水员不能依赖下游的转移。两者都必须取决于抽象,抽象不应取决于细节。详细信息应取决于抽象。
也就是说,在我们的几个部分中,存储库负责创建A service 的实例,A viewModel 它负责创建A *存储库的实例,现在更改。
注意:清洁体系结构的说明从Marcello Galhardo的优质artigo中删除。
使用Koud0
Koin
是根据Kotlin完全编写的注射框架的轻型框架,非常容易学习和使用。要使用它,我们需要了解您的术语:
-
模块:创建可用于提供全部依赖的
Koin
职责。 - 单身:创建一个可以在整个应用程序中用作唯一实例的单例。
-
Factory :提供一个
bean
定义,每次注入新实例。 - get():用于提供必要依赖性的类构建器中。
现在让我们通过Koud7文件将Koud0添加到我们的项目中:
def koinVersion = "3.2.2"
dependencies {
...
// Koin
implementation "io.insert-koin:koin-android:$koinVersion"
implementation "io.insert-koin:koin-android-compat:$koinVersion"
implementation "io.insert-koin:koin-androidx-workmanager:$koinVersion"
implementation "io.insert-koin:koin-androidx-navigation:$koinVersion"
...
}
要开始在我们的项目中使用Koin
,首先,我们需要为我们的PokemonRepository
创建一个界面,我们这样做以遵守干净的体系结构的原理。
package br.com.pokedex.domain.repository
import br.com.pokedex.domain.model.SinglePokemon
interface PokemonRepository {
suspend fun getSinglePokemon(id: Int): SinglePokemon
}
PokemonRepository
类将重命名为koud11并修改:
package br.com.pokedex.data.repository
import br.com.pokedex.data.api.PokemonApi
import br.com.pokedex.data.mapper.toModel
import br.com.pokedex.domain.repository.PokemonRepository
class PokemonRepositoryImpl(private val api: PokemonApi) : PokemonRepository {
override suspend fun getSinglePokemon(id: Int) = api.getSinglePokemon(id).toModel()
}
请注意,我们在此处删除了 service 的创建,我们之所以这样做,是因为Koin
将负责注入此依赖性,因此请应用依赖性逆转的原理。
我们将转到创建用例的创建,该将用作存储库和 viewModel 之间的桥梁。目前,它将非常简单,只会具有execute()
函数,该功能将访问koud9并返回koud15:
package br.com.pokedex.domain.interactor
import br.com.pokedex.domain.model.SinglePokemon
import br.com.pokedex.domain.repository.PokemonRepository
class GetSinglePokemonUseCase(private val repository: PokemonRepository) {
suspend fun execute(id: Int) : SinglePokemon {
return repository.getSinglePokemon(id)
}
}
package br.com.pokedex.presentation
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import br.com.pokedex.domain.interactor.GetSinglePokemonUseCase
import br.com.pokedex.domain.model.SinglePokemon
import br.com.pokedex.domain.repository.PokemonRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
private const val MIN_POKEMON_ID = 1
private const val MAX_POKEMON_ID = 151
class PokedexViewModel(private val useCase: GetSinglePokemonUseCase) : ViewModel() {
private val _pokemon = MutableLiveData<List<SinglePokemon>>()
val pokemon: LiveData<List<SinglePokemon>>
get() = _pokemon
fun getPokemon() {
viewModelScope.launch(Dispatchers.IO) {
val data = mutableListOf<SinglePokemon>()
for (i in MIN_POKEMON_ID..MAX_POKEMON_ID) {
data.add(useCase.execute(i))
}
withContext(Dispatchers.Main) {
_pokemon.postValue(data.toList())
}
}
}
}
好吧,让我们开始有效地使用koud0。最初,我们将创建一个称为di
的软件包(用于依赖依赖项注入的缩写),我们将构建依赖性注射文件。这是我们创建了InfrastructureModule.kt
文件,我们将创建一些在重要依赖的实例中进行的一些功能:Retrofit
和PokemonApi
:
package br.com.pokedex.di
import br.com.pokedex.BuildConfig
import br.com.pokedex.data.api.PokemonApi
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
private fun providePokemonApi(retrofit: Retrofit): PokemonApi {
return retrofit.create(PokemonApi::class.java)
}
private fun provideRetrofit(): Retrofit {
return Retrofit.Builder().run {
addConverterFactory(GsonConverterFactory.create())
baseUrl(BuildConfig.POKE_API)
build()
}
}
请注意,在provideRetrofit()
中,我们参考了一个POKE_API
字符串,它是pokâapi的基本URL,我们这样做是将其与下跌相距,我们将其放在koud7文件中:
def POKE_API = "POKE_API"
def URL_BASE_POKE_API = "\"https://pokeapi.co/api/v2/\""
android {
...
buildTypes {
debug {
applicationIdSuffix ".dev"
debuggable true
buildConfigField "String", POKE_API, URL_BASE_POKE_API
}
}
...
}
此时,当我们提供这些提供这些时,我们可以建立将为应用程序提供这些依赖性的沉闷,让我们称他为InfrastructureModule
,他将是一个依赖的集合每个函数将使用函数factory
返回,然后在必要时注射:
package br.com.pokedex.di
import br.com.pokedex.BuildConfig
import br.com.pokedex.data.api.PokemonApi
import br.com.pokedex.data.repository.PokemonRepositoryImpl
import br.com.pokedex.domain.repository.PokemonRepository
import org.koin.dsl.module
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
fun infrastructureModule() = module {
factory { provideRetrofit() }
factory { providePokemonApi(get()) }
factory<PokemonRepository> { PokemonRepositoryImpl(get()) }
}
private fun providePokemonApi(retrofit: Retrofit): PokemonApi {
return retrofit.create(PokemonApi::class.java)
}
private fun provideRetrofit(): Retrofit {
return Retrofit.Builder().run {
addConverterFactory(GsonConverterFactory.create())
baseUrl(BuildConfig.POKE_API)
build()
}
}
请注意,我们对职责进行编程,以便当请求PokemonRepository
实例时,我们将返回PokemonRepositoryImpl
的实例,使我们仅参考课程中的抽象,而不是实现£O。
仅剩下两个依赖性:GetSinglePokemonUseCase
和PokedexViewModel
,我们继续开发其他两个文件:DomainModule.kt
,for case 和koud32,用于 viewModel :
package br.com.pokedex.di
import br.com.pokedex.domain.interactor.GetSinglePokemonUseCase
import org.koin.dsl.module
fun domainModule() = module {
factory { GetSinglePokemonUseCase(get()) }
}
package br.com.pokedex.di
import br.com.pokedex.presentation.PokedexViewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
fun presentationModule() = module {
viewModel { PokedexViewModel(get()) }
}
package br.com.pokedex
import android.app.Application
class Pokedex : Application() {}
它是Application
子类,因为它是包含所有其他组件的应用程序的基类,例如Actives 和 services ,它是在其他类之前实例化的我们的应用程序是创建的。而且我们不能忘记将其添加到Koud35:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
...
<application
android:name=".Pokedex"
...
>
...
</application>
</manifest>
现在,让我们创建将处理所有这些依赖性的文件,即Injector.kt
,在其中我们开发了一个扩展函数 Application
称为koud38,该函数以所有必要的依赖性启动koud0:
package br.com.pokedex.di
import android.app.Application
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
fun Application.inject() {
startKoin {
androidLogger()
androidContext(this@inject)
modules(getModules())
}
}
fun getModules() = listOf(
infrastructureModule(),
domainModule(),
presentationModule()
)
除了使用startKoin
函数外,我们还使用androidLogger()
,它提供了一个简单的API来执行与Koin
,androidContext()
相关的日志,该日志将Context
添加到Koud0 coantment,modules()
,以携带职责的定义,并Koud47,他返回我们创建的所有转移。
好吧,我们已经可以在我们的Application
类中插入此函数,在onCreate()
函数中给予override
:
package br.com.pokedex
import android.app.Application
import br.com.pokedex.di.inject
class Pokedex : Application() {
override fun onCreate() {
super.onCreate()
inject()
}
}
因此,我们的项目以这种方式结构:
APON运行该应用程序,让我们意识到没有视觉上没有任何变化,但是我们知道我们的项目结构更好,没有核心,遵守了干净的体系结构的prinome!
帖子
在下一篇文章中,我们将使用分页库和Flow API提高Dex的性能,此外还可以使它们更美丽。
repositgoly no github:
前邮: