1. Introduction
Koin – một component mà được tạo ra để thay thế hoặc là một sự lựa chọn tuyệt vời thay cho Dagger khi viết app trong Kotlin . Koin được dùng để Inject các thành phần đến các nơi trong ứng dụng.
So sánh với Dagger
Ưu điểm : Cấu hình đơn giản hơn , ít có code generation ,v.v
Nhược điểm : Khi phát sinh lỗi thì lỗi không detail khiến chúng ta hơi khó debug hơn.
2. What you’ll build
Ở bài viết này chúng ta sẽ demo hoàn chỉnh cách sử dụng Koin để call api.
3. Config Koin và các thư viện liên quan và dựng demo
– Thêm thư viện vào build.gradle:
- Koin
- Retrofit
- Lifecycle +viewmodel
// Koin for Android
implementation "org.koin:koin-android:$koin_version"
// Koin Android Scope features
implementation "org.koin:koin-android-scope:$koin_version"
// Koin Android ViewModel features
implementation "org.koin:koin-android-viewmodel:$koin_version"
// Koin Android Experimental features
implementation "org.koin:koin-android-ext:$koin_version"
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// For ViewModel
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
– Cấu trúc thư mục và các file liên quan: 
– Tạo model UserEntity.kt
data class UserEntity(
val id: Long,
val login: String,
val avatar_url: String
)
– Tạo model LoadingState.kt để xử lý cho loading và error.
data class LoadingState private constructor(val status: Status, val msg: String? = null) {
companion object {
val LOADED =
LoadingState(Status.SUCCESS)
val LOADING =
LoadingState(Status.RUNNING)
fun error(msg: String?) = LoadingState(
Status.FAILED,
msg
)
}
enum class Status {
RUNNING,
SUCCESS,
FAILED
}
}
– Tạo class BaseViewModel.kt để dùng chung phần loading.
open class BaseViewModel : ViewModel() {
val loadingState = MutableLiveData<LoadingState>()
}
– Tạo ApiInterface.kt chứa các API cần sử dụng (retrofit)
interface ApiInterface {
@GET("users")
fun getUsers(): Call<List<UserEntity>>
}
– Tạo UserRepository.kt để gọi api
class UserRepository(private val apiInterface: ApiInterface) {
fun getAllUsers() = apiInterface.getUsers()
}
– Tạo UserViewModel.kt để lấy data từ UserRepository
class UserViewModel(private val repo: UserRepository) : BaseViewModel() {
private val _data = MutableLiveData<List<UserEntity>>()
val data: LiveData<List<UserEntity>>
get() = _data
fun fetchData() {
loadingState.postValue(LoadingState.LOADING)
repo.getAllUsers().enqueue(object : Callback<List<UserEntity>> {
override fun onFailure(call: Call<List<UserEntity>>, t: Throwable) {
loadingState.postValue(LoadingState.error(t.message))
}
override fun onResponse(
call: Call<List<UserEntity>>,
response: Response<List<UserEntity>>
) {
if (response.isSuccessful) {
_data.postValue(response.body())
loadingState.postValue(LoadingState.LOADED)
} else {
loadingState.postValue(LoadingState.error(response.errorBody().toString()))
}
}
})
}
}
– Phần quan trọng tạo file Module.kt : dùng Koin để tạo và các Module cho app.
val viewModelModule = module {
viewModel {
UserViewModel(get())
}
}
val repositoryModule = module {
single {
UserRepository(get())
}
}
val apiModule = module {
fun provideApi(retrofit: Retrofit): ApiInterface {
return retrofit.create(ApiInterface::class.java)
}
single { provideApi(get()) }
}
val retrofitModule = module {
fun provideGson(): Gson {
return GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY).create()
}
fun provideHttpClient(): OkHttpClient {
val okHttpClientBuilder = OkHttpClient.Builder()
return okHttpClientBuilder.build()
}
fun provideRetrofit(factory: Gson, client: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create(factory))
.client(client)
.build()
}
single { provideGson() }
single { provideHttpClient() }
single { provideRetrofit(get(), get()) }
}
– get() : gọi các thành phần cần thiết cho module của koin
Ex: UserViewModel(get()) Trong demo này UserViewModel cần sử dụng UserRepository và hàm get() của Koin sẽ làm điều đó.
– single: yêu cầu Koin tạo một singleton, và chỉ tạo 1 lần trong quá trình chạy ứng dụng.
2. Khởi tạo và sử dụng Koin
Giống như Dagger ta tạo 1 file Application và khai báo ở androidmanifest.xml
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidLogger()
androidContext(this@MyApplication)
modules(listOf(repositoryModule, viewModelModule, retrofitModule, apiModule))
}
}
}
Và giờ để sử dụng các thành phần viewmodel chỉ cần inject chúng vào activity hoặc fragment …
class MainActivity : AppCompatActivity() {
private val userViewModel : UserViewModel by viewModel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
userViewModel.fetchData()
userViewModel.data.observe(this, Observer {
Log.d( "userViewModel","userViewModel.data$it")
})
userViewModel.loadingState.observe(this, Observer {
Log.d( "userViewModel","loadingState$it")
})
}
}
Tada xong rồi các bạn ạ, 1 demo đơn giản về cách sử dụng Koin, có vẻ dễ hơn nhiều so với Dagger luôn nha.
Note: Bạn có thể tự demo bằng cách tạo thêm api getRepository.
@GET("/users/{userId}/repos")
fun getRepository(@Path("userId") userId: Int): Call<List<RepoEntity>>
Tham khảo tại: https://medium.com/swlh/dependency-injection-with-koin-6b6364dc8dba