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