For those of you who have used MVP before, if you have used Dagger, the dependency injection framework, you will see how difficult it is to use, but I won’t cover the use of Dagger here. Let’s learn about koin, the new dependency injection framework on Kotlin

This article uses Kotlin + Jetpack

According to Google, an APP should have a UI layer, a ViewModel layer, and a Repository layer. For a brief introduction, the UI holds a reference to the ViewModel, the ViewModel holds a reference to the Repository, Repository holds both the database Dao layer and the Network layer

Using Koin

Introducing dependencies:

def retrofit_version = 'server'
def moshiVersion = '1.8.0 comes with'
def koin_version = "2.0.1"
def viewmodel_ktx = '2.2.0 - alpha03'
def kotlinCoroutineVersion = "1.0.1"// Retrofit and Moshi transformations parse the results of responses into object implementation"com.squareup.retrofit2:retrofit:${retrofit_version}"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"// moshi parsing JSON implementation"com.squareup.moshi:moshi-kotlin:$moshiVersion"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"Koin dependency injection implementation"org.koin:koin-android:$koin_version"
implementation "org.koin:koin-android-viewmodel:$koin_version"// ViewModel Implementation that can use coroutines and observe the lifecycle"androidx.lifecycle:lifecycle-viewmodel-ktx:$viewmodel_ktx"/ / coroutines implementation"org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutineVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutineVersion"
    
    
Copy the code

Finish see needed to rely on, some students may ask, why no Retrofit adapter, like implementation ‘com. Squareup. Retrofit2: adapter – rxjava2:2.3.0’, etc., here want to introduce is coroutines, The suspend function is already supported in version 2.6.0 of Retrofit.

To use Koin, we need to register with the Application:

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

In Application, three modules are registered, which correspond to our warehouse layer, ViewModel layer and network layer respectively

Create a new folder di, and we’ll put three modules here

// 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>>
}

Copy the code

The following code is very simple: each time we create a new ViewModel, Repository, we declare viewModelModule. kt and RepoModule. MainViewModel(get()); MainViewModel(get()); Because our ViewModel layer is supposed to hold references to the repository layer, using get() will look for what we need in all the injected classes. Simple.

In the API, we use the suspend keyword when calling the interface, and the result is no longer a Call. We only need to open a coroutine to get the data we need, and then use LiveData to send the data to our UI

Let’s look at the code:

// 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() {// When the Activity is destroyed, Viewmodelscope.launch {countryLiveData.value = fetch(rebo.getCountryData ())}} class MainActivity:AppCompatActivityPrivate val MainViewModel by viewModel<MainViewModel>() override public 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
        })
        
    }
}
Copy the code

conclusion

  • With Koin, it is much easier when the business is complex and you no longer have to pass it around through constructors

  • Version 2.6.0 of Retrofit already supports coroutines by default, and some of you might think that by suspending an exception, you can use a try-catch when you call a repository method in the ViewModel layer. Or you can do a BaseViewModel and do it all together

  • With LiveData + ViewModel + Coroutines, you don’t have to worry about switching threads when a network request is made, how to cancel the request when a page is destroyed, etc. Of course, the ViewModel needs to use the ViewModel that gradle relies on