mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor(di): adopt @KoinApplication with startKoin<T>() compiler plugin API (#5152)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
0f900fe7d7
commit
8e5d99410c
5 changed files with 67 additions and 14 deletions
|
|
@ -12,15 +12,26 @@ This skill covers dependency injection (Koin Annotations 4.2.x) and JetBrains Na
|
|||
4. **Resolution:** Resolve app-layer wrappers via `koinViewModel()` or injected bindings within Compose navigation graphs.
|
||||
|
||||
### Anti-Patterns
|
||||
- **A1 Module Compile Safety:** Do **not** enable A1 `compileSafety`. We rely on Koin's A3 full-graph validation (`startKoin` / `VerifyModule`) because of our decoupled Clean Architecture design (interfaces in one module, implemented in another).
|
||||
- **A1 Module Compile Safety:** Do **not** enable `compileSafety`. It is a single boolean that enables A1 per-module checks — there is no separate A3 full-graph mode. Runtime graph verification is handled by `KoinVerificationTest` and `DesktopKoinTest` instead.
|
||||
- **Default Parameters:** Do **not** expect Koin to inject default parameters automatically. The K2 plugin's `skipDefaultValues = true` behavior skips parameters with default Kotlin values.
|
||||
|
||||
### Koin Startup Pattern (K2 Compiler Plugin)
|
||||
The project uses the **K2 Compiler Plugin** (`koin-compiler-plugin`, not KSP). The correct canonical startup for this path is:
|
||||
The project uses the **K2 Compiler Plugin** (`koin-compiler-plugin`, not KSP). The canonical startup uses the plugin's typed `startKoin<T>()` stub, which the plugin transforms at compile time via IR:
|
||||
```kotlin
|
||||
startKoin { modules(AppKoinModule().module()) }
|
||||
// Bootstrap class — separate from @Module, references the root module graph
|
||||
@KoinApplication(modules = [AppKoinModule::class])
|
||||
object AndroidKoinApp
|
||||
|
||||
// In Application.onCreate()
|
||||
startKoin<AndroidKoinApp> {
|
||||
androidContext(this@MeshUtilApplication)
|
||||
workManagerFactory()
|
||||
}
|
||||
```
|
||||
Do **not** use `@KoinApplication` — that annotation is part of the **KSP annotations path** (`koin-ksp-compiler`) and generates a `startKoin()` extension via KSP. It is incompatible with the K2 plugin approach. The two paths are mutually exclusive; the project has deliberately chosen K2 for compile-time wiring without KSP overhead.
|
||||
- `@KoinApplication` goes on a **dedicated bootstrap object**, not on a `@Module` class.
|
||||
- `startKoin<T>()` (from `org.koin.plugin.module.dsl`) is a compiler plugin stub — if the plugin isn't applied, it throws `NotImplementedError`.
|
||||
- `stopKoin()` uses the standard runtime API (`org.koin.core.context.stopKoin`).
|
||||
- `compileSafety` must stay **disabled** — it enables A1 per-module checks that break our inverted-dependency architecture. There is no separate A3 full-graph flag.
|
||||
|
||||
## Navigation 3
|
||||
|
||||
|
|
@ -39,6 +50,7 @@ Do **not** use `@KoinApplication` — that annotation is part of the **KSP annot
|
|||
|
||||
## Reference Anchors
|
||||
- **App Startup / Koin Bootstrap:** `app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt`
|
||||
- **DI Bootstrap Object:** `app/src/main/kotlin/org/meshtastic/app/di/AndroidKoinApp.kt`
|
||||
- **DI App Wiring:** `app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt`
|
||||
- **Shared Routes:** `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt`
|
||||
- **Desktop Nav Shell:** `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
|
||||
|
|
|
|||
|
|
@ -37,9 +37,8 @@ import kotlinx.coroutines.withTimeout
|
|||
import org.koin.android.ext.android.get
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.androidx.workmanager.koin.workManagerFactory
|
||||
import org.koin.core.context.startKoin
|
||||
import org.meshtastic.app.di.AppKoinModule
|
||||
import org.meshtastic.app.di.module
|
||||
import org.koin.plugin.module.dsl.startKoin
|
||||
import org.meshtastic.app.di.AndroidKoinApp
|
||||
import org.meshtastic.core.common.ContextServices
|
||||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.repository.MeshPrefs
|
||||
|
|
@ -64,10 +63,9 @@ open class MeshUtilApplication :
|
|||
super.onCreate()
|
||||
ContextServices.app = this
|
||||
|
||||
startKoin {
|
||||
startKoin<AndroidKoinApp> {
|
||||
androidContext(this@MeshUtilApplication)
|
||||
workManagerFactory()
|
||||
modules(AppKoinModule().module())
|
||||
}
|
||||
|
||||
// Schedule periodic MeshLog cleanup
|
||||
|
|
|
|||
26
app/src/main/kotlin/org/meshtastic/app/di/AndroidKoinApp.kt
Normal file
26
app/src/main/kotlin/org/meshtastic/app/di/AndroidKoinApp.kt
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.app.di
|
||||
|
||||
import org.koin.core.annotation.KoinApplication
|
||||
|
||||
/**
|
||||
* Root Koin bootstrap for Android. The K2 compiler plugin uses this to discover the full module graph when
|
||||
* [org.koin.plugin.module.dsl.startKoin] is called with this type parameter.
|
||||
*/
|
||||
@KoinApplication(modules = [AppKoinModule::class])
|
||||
object AndroidKoinApp
|
||||
|
|
@ -25,6 +25,7 @@ import androidx.work.WorkerParameters
|
|||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.HttpClientEngine
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import org.koin.plugin.module.dsl.koinApplication
|
||||
import org.koin.test.verify.definition
|
||||
import org.koin.test.verify.injectedParameters
|
||||
import org.koin.test.verify.verify
|
||||
|
|
@ -60,4 +61,19 @@ class KoinVerificationTest {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyTypedBootstrapLoadsModuleGraph() {
|
||||
// koinApplication<T>() is a K2 compiler plugin stub. If the plugin fails to
|
||||
// transform it, the stub throws NotImplementedError at runtime. This test
|
||||
// validates that the production bootstrap path is correctly transformed by
|
||||
// successfully creating and closing the generated Koin application.
|
||||
val app = koinApplication<AndroidKoinApp>()
|
||||
try {
|
||||
// No-op: reaching this point proves the typed bootstrap path did not
|
||||
// throw and the generated application could be created.
|
||||
} finally {
|
||||
app.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@ class KoinConventionPlugin : Plugin<Project> {
|
|||
|
||||
// Configure Koin K2 Compiler Plugin (0.4.0+)
|
||||
extensions.configure(KoinGradleExtension::class.java) {
|
||||
// Meshtastic heavily utilizes dependency inversion across KMP modules. Koin's A1
|
||||
// per-module safety checks strictly enforce that all dependencies must be explicitly
|
||||
// provided or included locally. This breaks decoupled Clean Architecture designs.
|
||||
// We disable compile safety globally to properly rely on Koin's A3 full-graph
|
||||
// validation which perfectly handles inverted dependencies at the composition root.
|
||||
// Meshtastic uses dependency inversion across KMP modules — interfaces in
|
||||
// commonMain, implementations wired at the composition root. Koin's compileSafety
|
||||
// flag enables A1 per-module checks that treat every module as self-contained,
|
||||
// which breaks this pattern. There is no separate flag for A3 full-graph
|
||||
// validation. Until Koin exposes granular safety levels we keep this disabled;
|
||||
// runtime graph verification is handled by KoinVerificationTest instead.
|
||||
compileSafety.set(false)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue