Skip to content

Commit

Permalink
add onKoinStartup function to help start with AndroidX startup
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaudgiuliani committed Sep 16, 2024
1 parent 673e956 commit e0adb2e
Show file tree
Hide file tree
Showing 14 changed files with 240 additions and 43 deletions.
26 changes: 26 additions & 0 deletions docs/reference/koin-android/start.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,29 @@ startKoin {
}
```

## Start Koin with Androidx Startup (4.0)

By using Gradle packge `koin-androidx-startup`, we can use `onKoinStartup` function durectly in `init` block of your Application class:

```kotlin
class MainApplication : Application() {

init {
// Use AndroidX Startup for Koin
onKoinStartup {
androidContext(this@MainApplication)
modules(allModules)
}
}

override fun onCreate() {
super.onCreate()
}
}
```

This replaces the `startKoin` function that is usally used in `onCreate`.

:::info
Gain over from `onKoinStartup` to regular `startKoin` can go over 30% of time gained, for startup time.
:::
31 changes: 14 additions & 17 deletions docs/setup/koin.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Here are the currently available versions:
| koin-android-compat | [![Maven Central](https://img.shields.io/maven-central/v/io.insert-koin/koin-android-compat)](https://mvnrepository.com/artifact/io.insert-koin/koin-android-compat) |
| koin-androidx-navigation | [![Maven Central](https://img.shields.io/maven-central/v/io.insert-koin/koin-androidx-navigation)](https://mvnrepository.com/artifact/io.insert-koin/koin-androidx-navigation) |
| koin-androidx-workmanager | [![Maven Central](https://img.shields.io/maven-central/v/io.insert-koin/koin-androidx-workmanager)](https://mvnrepository.com/artifact/io.insert-koin/koin-androidx-workmanager) |
| koin-androidx-startup | [![Maven Central](https://img.shields.io/maven-central/v/io.insert-koin/koin-androidx-startup)](https://mvnrepository.com/artifact/io.insert-koin/koin-androidx-startup) |
| koin-compose | [![Maven Central](https://img.shields.io/maven-central/v/io.insert-koin/koin-compose)](https://mvnrepository.com/artifact/io.insert-koin/koin-compose) |
| koin-compose-viewmodel | [![Maven Central](https://img.shields.io/maven-central/v/io.insert-koin/koin-compose-viewmodel)](https://mvnrepository.com/artifact/io.insert-koin/koin-compose-viewmodel) |
| koin-compose-viewmodel-navigation| [![Maven Central](https://img.shields.io/maven-central/v/io.insert-koin/koin-compose-viewmodel-navigation)](https://mvnrepository.com/artifact/io.insert-koin/koin-compose-viewmodel-navigation) |
Expand Down Expand Up @@ -128,40 +129,36 @@ dependencies {
implementation("io.insert-koin:koin-androidx-workmanager:$koin_android_version")
// Navigation Graph
implementation("io.insert-koin:koin-androidx-navigation:$koin_android_version")
// App Startup
implementation("io.insert-koin:koin-androidx-startup:$koin_android_version")
}
```

:::info
From now you can continue on Koin Tutorials to learn about using Koin: [Android App Tutorial](/docs/quickstart/android-viewmodel)
:::

### **Android Jetpack Compose**
### **Jetpack Compose or Compose Multiplatform**

Add `koin-compose` dependency to your multiplatform application, for use Koin & Compose API:

```groovy
dependencies {
implementation("io.insert-koin:koin-androidx-compose:$koin_android_compose_version")
implementation("io.insert-koin:koin-compose:$koin_version")
implementation("io.insert-koin:koin-compose-viewmodel:$koin_version")
implementation("io.insert-koin:koin-compose-viewmodel-navigation:$koin_version")
}
```

You are now ready to start Koin in your `Application` class:
If you are using pure Android Jetpack Compose, you can go with

```kotlin
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()

startKoin {
modules(appModule)
}
}
```groovy
dependencies {
implementation("io.insert-koin:koin-androidx-compose:$koin_version")
implementation("io.insert-koin:koin-androidx-compose-navigation:$koin_version")
}
```

:::info
From now you can continue on Koin Tutorials to learn about using Koin: [Android Compose App Tutorial](/docs/quickstart/android-compose)
:::


### **Kotlin Multiplatform**

Add `koin-core` dependency to your multiplatform application, for shared Kotlin part:
Expand Down
3 changes: 2 additions & 1 deletion examples/androidx-samples/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ android {
dependencies {

implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "androidx.work:work-runtime-ktx:2.9.0"
implementation "androidx.work:work-runtime-ktx:2.9.1"
implementation 'com.squareup.leakcanary:leakcanary-android:2.7'
testImplementation 'androidx.arch.core:core-testing:2.2.0'
testImplementation "junit:junit:4.13.2"
Expand All @@ -68,6 +68,7 @@ dependencies {
implementation "io.insert-koin:koin-core-coroutines"
implementation "io.insert-koin:koin-androidx-workmanager"
implementation "io.insert-koin:koin-androidx-navigation"
implementation "io.insert-koin:koin-androidx-startup"
testImplementation "io.insert-koin:koin-test-junit4"
testImplementation "io.insert-koin:koin-android-test"
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,43 @@ import org.koin.android.ext.koin.androidFileProperties
import org.koin.android.ext.koin.androidLogger
import org.koin.androidx.fragment.koin.fragmentFactory
import org.koin.androidx.workmanager.koin.workManagerFactory
import org.koin.androix.startup.KoinStartup.onKoinStartup
import org.koin.core.context.startKoin
import org.koin.core.lazyModules
import org.koin.core.logger.Level
import org.koin.sample.sandbox.di.allModules

class MainApplication : Application() {

override fun onCreate() {
super.onCreate()

startKoin {
class MainApplication : Application() {

init {
onKoinStartup {
androidLogger(Level.DEBUG)
androidContext(this@MainApplication)
androidFileProperties()
fragmentFactory()
workManagerFactory()

// modules(allModules)
// Lazy Modules
lazyModules(allModules)
modules(allModules)
}
}

companion object {
var startTime: Long = 0
}

override fun onCreate() {
super.onCreate()
startTime = System.currentTimeMillis()
// startKoin {
// androidLogger(Level.DEBUG)
// androidContext(this@MainApplication)
// androidFileProperties()
// fragmentFactory()
// workManagerFactory()
//
// modules(allModules)
// }

//TODO Load/Unload Koin modules scenario cases
cancelPendingWorkManager(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.koin.core.module.dsl.*
import org.koin.core.module.includes
import org.koin.core.qualifier.named
import org.koin.dsl.lazyModule
import org.koin.dsl.module
import org.koin.sample.sandbox.components.Counter
import org.koin.sample.sandbox.components.SCOPE_ID
import org.koin.sample.sandbox.components.SCOPE_SESSION
Expand All @@ -32,7 +33,7 @@ import org.koin.sample.sandbox.scope.ScopedFragment
import org.koin.sample.sandbox.workmanager.SimpleWorker
import org.koin.sample.sandbox.workmanager.SimpleWorkerService

val appModule = lazyModule {
val appModule = module {

singleOf(::SimpleServiceImpl) { bind<SimpleService>() }
singleOf(::DumbServiceImpl) {
Expand All @@ -42,7 +43,7 @@ val appModule = lazyModule {
factory { RandomId() }
}

val mvpModule = lazyModule {
val mvpModule = module {
//factory { (id: String) -> FactoryPresenter(id, get()) }
factoryOf(::FactoryPresenter)

Expand All @@ -52,7 +53,7 @@ val mvpModule = lazyModule {
}
}

val mvvmModule = lazyModule {
val mvvmModule = module {

viewModelOf(::SimpleViewModel)// { (id: String) -> SimpleViewModel(id, get()) }
viewModelOf(::SimpleViewModel) { named("vm1") } //{ (id: String) -> SimpleViewModel(id, get()) }
Expand Down Expand Up @@ -92,7 +93,7 @@ val mvvmModule = lazyModule {
}
}

val scopeModule = lazyModule {
val scopeModule = module {
scope(named(SCOPE_ID)) {
scopedOf(::Session) {
named(SCOPE_SESSION)
Expand All @@ -105,7 +106,7 @@ val scopeModule = lazyModule {
}
}

val scopeModuleActivityA = lazyModule {
val scopeModuleActivityA = module {
scope<ScopedActivityA> {
fragmentOf(::ScopedFragment)
scopedOf(::Session)
Expand All @@ -116,19 +117,19 @@ val scopeModuleActivityA = lazyModule {
}
}

val workerServiceModule = lazyModule {
val workerServiceModule = module {
singleOf(::SimpleWorkerService)
}

val workerScopedModule = lazyModule {
val workerScopedModule = module {
workerOf(::SimpleWorker)// { SimpleWorker(get(), androidContext(), it.get()) }
}

val navModule = lazyModule {
val navModule = module {
viewModelOf(::NavViewModel)
viewModelOf(::NavViewModel2)
}

val allModules = lazyModule {
val allModules = module {
includes(appModule, mvpModule, mvvmModule , scopeModule , workerServiceModule , workerScopedModule , navModule , scopeModuleActivityA)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package org.koin.sample.sandbox.main

import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import org.koin.android.ext.android.get
import org.koin.android.ext.android.getKoin
import org.koin.android.ext.android.inject
import org.koin.core.qualifier.named
import org.koin.sample.sandbox.MainApplication
import org.koin.sample.sandbox.R
import org.koin.sample.sandbox.components.APP_TITLE
import org.koin.sample.sandbox.components.main.*
Expand Down Expand Up @@ -46,4 +48,13 @@ class MainActivity : AppCompatActivity() {
navigateTo<MVPActivity>(isRoot = true)
}
}

override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
val endTime = System.currentTimeMillis()
val startupTime = endTime - MainApplication.startTime
Log.i("[MEASURE]","App startup time - $startupTime ms")
}
}
}
5 changes: 1 addition & 4 deletions examples/androidx-samples/src/test/java/CheckModulesTest.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import org.junit.Test
import org.koin.android.test.verify.androidVerify
import org.koin.android.test.verify.verify
import org.koin.sample.sandbox.di.allModules
import org.koin.sample.sandbox.di.appModule
import org.koin.sample.sandbox.di.mvpModule

class CheckModulesTest {

@Test
fun `Verify Configuration`() {
allModules.value.androidVerify()
allModules.androidVerify()
}
}
48 changes: 48 additions & 0 deletions projects/android/koin-androidx-startup/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinAndroid)
}

val androidCompileSDK : String by project
val androidMinSDK : String by project

android {
namespace = "org.koin.androidx.startup"
compileSdk = androidCompileSDK.toInt()
defaultConfig {
minSdk = androidMinSDK.toInt()
}
buildFeatures {
buildConfig = false
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}

tasks.withType<KotlinCompile>().all {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_1_8)
}
}

dependencies {
api(project(":android:koin-android"))
api(libs.androidx.workmanager)

// Test
testImplementation(libs.test.junit)
testImplementation(libs.test.mockito)
}

// android sources
val sourcesJar: TaskProvider<Jar> by tasks.registering(Jar::class) {
archiveClassifier.set("sources")
from(android.sourceSets.map { it.java.srcDirs })
}

apply(from = file("../../gradle/publish-android.gradle.kts"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.koin.androidx.startup" xmlns:tools="http://schemas.android.com/tools">
<application>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- This entry makes ExampleLoggerInitializer discoverable. -->
<meta-data android:name="org.koin.androix.startup.KoinInitializer"
android:value="androidx.startup" />
</provider>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2017-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.koin.androix.startup

import android.content.Context
import androidx.startup.Initializer
import org.koin.core.Koin
import org.koin.core.annotation.KoinExperimentalAPI
import org.koin.core.context.startKoin

/**
* KoinInitializer handle Initializer for Koin startup process
*
* @author Arnaud Giuliani
*/
class KoinInitializer : Initializer<Koin> {

@OptIn(KoinExperimentalAPI::class)
override fun create(context: Context): Koin {
return KoinStartup.koinAppDeclaration?.let {
startKoin(it).koin
} ?: error("KoinInitializer can't start Koin configuration. Please use KoinStartup.onKoinStartup() function to register your Koin application.")
}

override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
Loading

0 comments on commit e0adb2e

Please sign in to comment.