Android dependency injection koin example
The following is a walk through for setting up dependency injection using the library Koin for an Android project. The finished project will be a one screen app, where you can type in a github account name and it will fetch the account detail and display it.
The gradle file
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example.android_di_koin_example"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
ext.versions = [
"androidx_lifecycle": "2.2.0-rc03",
'koin': '2.0.1' ,
'retrofit': '2.6.1' ,
'okhttp': '4.0.1'
]
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
// Lifecycle
implementation "androidx.lifecycle:lifecycle-livedata:$versions.androidx_lifecycle"
implementation "androidx.lifecycle:lifecycle-viewmodel:$versions.androidx_lifecycle"
implementation "androidx.lifecycle:lifecycle-extensions:$versions.androidx_lifecycle"
kapt "androidx.lifecycle:lifecycle-compiler:$versions.androidx_lifecycle"
//Okhttp
implementation "com.squareup.okhttp3:okhttp:$versions.okhttp"
implementation "com.squareup.okhttp3:logging-interceptor:$versions.okhttp"
//Retrofit
implementation "com.squareup.retrofit2:retrofit:$versions.retrofit"
implementation "com.squareup.retrofit2:converter-gson:$versions.retrofit"
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
// coroutines
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-rc03"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0'
// Retrofit 2 for network tasks
implementation 'com.squareup.okhttp3:logging-interceptor:4.0.1'
implementation 'com.google.code.gson:gson:2.8.6'
//Koin
implementation "org.koin:koin-android:$versions.koin"
implementation "org.koin:koin-androidx-scope:$versions.koin"
implementation "org.koin:koin-androidx-viewmodel:$versions.koin"
}
GithubAccount.kt, the data class for modeling the github account data.
import com.google.gson.annotations.SerializedName
data class GithubAccount(
@SerializedName("login") var login : String = "",
@SerializedName("id") var id : Int = 0,
@SerializedName("avatar_url") var avatarUrl : String = "",
@SerializedName("created_at") var createdAt : String = "",
@SerializedName("updated_at") var updatedAt : String = "")
GithubApi.kt, the Retrofit interface for the github api.
import kotlinx.coroutines.Deferred
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path
interface GithubApi {
@GET("/users/{username}")
fun fetchGithubAccountAsync(@Path("username") username: String): Deferred>
}
GithubRepository.kt, the repository for managing the api calls.
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext
class GithubRepository(private val githubApi: GithubApi) {
suspend fun getGithubAccountAsync(accountName: String): Deferred {
return withContext(Dispatchers.IO) {
async {
try {
// for demo purpose, hence no error checking
githubApi.fetchGithubAccountAsync(accountName).await().body() as GithubAccount
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}
}
}
The above are just for setting up the project, here starts the Koin configuration. First, the NetworkModule.kt, provides the components needed for REST services. single means provide a singleton.
val networkModule = module {
// Provide Gson
single {
GsonBuilder().create()
}
// Provide HttpLoggingInterceptor
single {
HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
}
// Provide OkHttpClient
single {
val cacheDir = File((get() as MyApp).cacheDir, "http")
val cache = Cache(
cacheDir,
10 * 1024 * 1024 // 10 MB
)
OkHttpClient.Builder()
.cache(cache)
.addInterceptor(get())
.build()
}
// Provide Retrofit
single {
Retrofit.Builder()
.client(get())
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create(get()))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
}
// Provide GithubApi
single {
get().create(GithubApi::class.java)
}
}
RepositoryModule.kt
val repositoryModule = module {
// Provide GithubRepository
single {
GithubRepository(get())
}
}
ViewModelModule.kt
val vmModule = module {
// Provide MainActivityViewModel
viewModel { MainActivityViewModel(get()) }
}
Initializing Koin with the desired modules in the application class.
class MyApp : Application() {
private val appModules = listOf(
repositoryModule,
networkModule,
vmModule
)
override fun onCreate() {
super.onCreate()
startKoin {
androidLogger()
androidContext(this@MyApp)
modules(appModules)
}
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private val mainActivityViewModel: MainActivityViewModel by viewModel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainActivityViewModel.githubAccount.observe(this, Observer {
tv_content.text = it.toString()
})
btn_fetch.setOnClickListener {
mainActivityViewModel.fetchAccount(et_account.text.toString())
}
}
}
MainActivityViewModel.kt
class MainActivityViewModel
constructor(private val githubRepository: GithubRepository): ViewModel() {
private val _githubAccount = MutableLiveData()
val githubAccount: LiveData get() = _githubAccount
fun fetchAccount(accountName: String) {
viewModelScope.launch {
val githubAccount = githubRepository.getGithubAccountAsync(accountName).await()
githubAccount?.let {
_githubAccount.value = it
}
}
}
}
Search within Codexpedia
Search the entire web