Skip to content

延遲模組與背景載入

延遲模組(Lazy modules)支援非同步、平行的模組載入,以提升啟動效能。您可以延遲並平行化模組初始化,而不是在啟動時同步載入所有模組。

INFO

本頁面使用 Koin 編譯器外掛程式 DSL (single<T>())。請參閱 編譯器外掛程式設定 以進行配置。

什麼是延遲模組?

延遲模組會延遲模組註冊和執行個體建立,直到明確載入為止。它們特別適用於:

  • 大型應用程式 - 將初始化拆分到多個執行緒中。
  • 效能最佳化 - 減少啟動時間。
  • 條件式功能 - 僅在需要時載入模組。
  • 背景初始化 - 非同步載入非關鍵模組。

定義延遲模組

使用 lazyModule 函式建立延遲模組:

kotlin
// 延遲模組 - 直到明確要求時才會載入
val networkModule = lazyModule {
    single<ApiClient>()
    single<NetworkMonitor>()
}

val databaseModule = lazyModule {
    single<Database>()
    single<UserDao>()
}

組合延遲模組

延遲模組與一般模組一樣支援 includes()

kotlin
val dataModule = lazyModule {
    single<UserRepository>()
}

val featureModule = lazyModule {
    includes(dataModule)  // 包含其他延遲模組
    single<FeatureService>()
}

INFO

延遲模組在透過 lazyModules() 函式載入之前,不會分配任何資源。

載入延遲模組

在您的 Koin 配置中使用 lazyModules() 載入延遲模組。

基本載入

kotlin
val analyticsModule = lazyModule {
    single<AnalyticsService>()
}

val reportingModule = lazyModule {
    single<CrashReporter>()
}

startKoin {
    // 立即載入關鍵模組
    modules(coreModule, networkModule)

    // 在背景載入非關鍵模組
    lazyModules(analyticsModule, reportingModule)
}

平行載入 (4.2.0+)

自 4.2.0 版本起,多個延遲模組會平行載入,每個模組都在自己的協同程式中執行:

kotlin
val module1 = lazyModule { single<DatabaseService>() }
val module2 = lazyModule { single<NetworkService>() }
val module3 = lazyModule { single<AnalyticsService>() }

startKoin {
    // 所有三個模組同時載入!
    lazyModules(module1, module2, module3)
}

效能影響:

情境4.2.0 之前(循序)4.2.0 之後(平行)
1 個模組 @ 100ms100ms100ms
3 個模組 @ 每個 100ms300ms~100ms
10 個模組 @ 每個 100ms1000ms~100ms

等待完成

所有平台:waitAllStartJobs()

kotlin
startKoin {
    lazyModules(module1, module2, module3)
}

val koin = KoinPlatform.getKoin()

// 阻塞直到所有延遲模組載入完成
koin.waitAllStartJobs()

// 現在可以安全地使用來自延遲模組的相依性
val service = koin.get<AnalyticsService>()

平台行為:

  • JVM/Native:使用 runBlocking 進行真正的阻塞。
  • JS:使用 GlobalScope.promise(非真正阻塞,會記錄警告)。

僅限 JVM:runOnKoinStarted()

kotlin
startKoin {
    lazyModules(analyticsModule)
}

// 僅限 JVM 的回呼
KoinPlatform.getKoin().runOnKoinStarted { koin ->
    // 在所有延遲模組完成載入後執行
    koin.get<AnalyticsService>().trackAppStart()
}

暫停替代方案:awaitAllStartJobs()

適用於協同程式上下文或不支援阻塞的平台:

kotlin
suspend fun initializeApp() {
    startKoin {
        lazyModules(module1, module2)
    }

    // 等待且不阻塞
    KoinPlatform.getKoin().awaitAllStartJobs()

    // 安全地繼續執行
    println("所有模組已載入!")
}

自訂發送器

控制在哪個發送器(dispatcher)上執行延遲模組載入:

kotlin
import kotlinx.coroutines.Dispatchers

startKoin {
    // 在 IO 發送器上載入,而不是 Default
    lazyModules(
        databaseModule,
        networkModule,
        dispatcher = Dispatchers.IO
    )
}

常見的發送器選擇:

  • Dispatchers.Default - CPU 密集型工作(預設)。
  • Dispatchers.IO - I/O 操作、檔案存取、網路。
  • Dispatchers.Main - UI 更新(Android/Desktop)。

INFO

如果未指定,預設發送器為 Dispatchers.Default

實際範例

kotlin
// 核心模組 - 立即載入
val coreModule = module {
    single<AppConfig>()
    single<UserSession>()
}

// 功能模組 - 在背景載入
val analyticsModule = lazyModule {
    single<AnalyticsEngine>()
    single<EventTracker>()
}

val networkingModule = lazyModule {
    single<ApiClient>()
    single<WebSocketManager>()
}

val databaseModule = lazyModule {
    single<Database>()
    single<UserDao>()
}

// Android 應用程式
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@MyApp)

            // 關鍵模組立即載入
            modules(coreModule)

            // 非關鍵模組在背景平行載入
            lazyModules(
                analyticsModule,
                networkingModule,
                databaseModule,
                dispatcher = Dispatchers.IO
            )
        }

        // 選用:等待背景載入完成
        lifecycleScope.launch {
            KoinPlatform.getKoin().awaitAllStartJobs()
            Log.d("Koin", "所有模組已載入!")
        }
    }
}

重要限制

避免交叉相依

延遲模組與一般模組應保持獨立。不要建立從一般模組到延遲模組的相依性:

kotlin
// ❌ 錯誤範例 - mainModule 依賴於延遲模組
val lazyAnalytics = lazyModule {
    single { AnalyticsService() }
}

val mainModule = module {
    single { AppController(get<AnalyticsService>()) }  // 可能會失敗!
}

startKoin {
    modules(mainModule)
    lazyModules(lazyAnalytics)
}
kotlin
// ✅ 正確範例 - 保持相依性分離
val lazyAnalytics = lazyModule {
    single { AnalyticsService() }
}

val mainModule = module {
    single { AppController() }
}

startKoin {
    modules(mainModule)
    lazyModules(lazyAnalytics)
}

WARNING

Koin 目前不會驗證一般模組與延遲模組之間的相依性。請確保一般模組不依賴於延遲模組的定義。

最佳實務:載入順序

  1. 立即模組 - 啟動時需要的關鍵服務。
  2. 延遲模組 - 非關鍵、可延後的服務。
  3. 必要時等待 - 在存取延遲定義之前,使用 waitAllStartJobs()

何時使用延遲模組

適合的使用案例

  • 分析/追蹤 - 並非核心功能所需。
  • 損毀報告 - 可以在背景初始化。
  • 功能模組 - 按需載入的模組化功能。
  • 資料庫/網路 - 可以延後的重量級初始化。
  • 大型應用程式 - 將啟動負載拆分到不同執行緒。

不建議使用

  • 核心服務 - 立即需要的關鍵相依性。
  • 小型應用程式 - 額外開銷可能會超過其收益。
  • 緊密耦合的模組 - 當模組之間有許多交叉相依時。

API 參考

函式平台描述
lazyModules()全部在背景載入延遲模組
waitAllStartJobs()全部阻塞直到所有延遲模組載入完成
awaitAllStartJobs()全部暫停直到所有延遲模組載入完成
runOnKoinStarted()僅限 JVM載入完成後的回呼

延伸閱讀