簡介

承續之前的程式碼 (Dependency Injection – 1 – 程式碼範例 – 自行撰寫將 SharedPreferences 實例依賴注入的程式碼),我們使用 Koin 來重構程式。

依賴

libs.versions.toml (修改)

[versions]
...

koin = "3.5.3"

[libraries]
...

koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" }
koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" }
koin-androidx-compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin" }

...

[bundles]
...

koin-compose = ["koin-core", "koin-android", "koin-androidx-compose"]

build.gradle.kts (Module level :app) (修改)

...

dependencies {

    ...

    implementation(libs.bundles.koin.compose)

}

程式碼範例

AppModule.kt (修改)

//interface AppModule {
//    val sharedPref: SharedPreferences
//}
//
//class AppModuleImpl( private val appContext: Context): AppModule {
//    override val sharedPref: SharedPreferences = appContext.getSharedPreferences("mypref", Context.MODE_PRIVATE)
//}

val appModule = module {
    // 完整寫法
//    single<SharedPreferences> {
//        androidApplication().getSharedPreferences("mypref", Context.MODE_PRIVATE)
//    }
    // 因為可以從產生的實例類別對應到 MainViewModel 的參數型別,所以可以省略類別
    
    single {
        androidApplication().getSharedPreferences("mypref", Context.MODE_PRIVATE)
    }
    // 但如果我們用的是子類別 (例如: androidx.security.crypto.EncryptedSharedPreferences),
    // 就要指定要注入的類別
//    single<SharedPreferences> {
//        EncryptedSharedPreferences(
//            androidApplication(),
//            "mypref",
//            MasterKey(androidApplication()),
//            EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
//            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
//        )
//    }

    viewModel {
        MainViewModel(get())
    }
    // 這種寫法也可以
    // viewModelOf(::MainViewModel)
}

MyApp.kt (修改)

class MyApp: Application() {
//    companion object {
//        lateinit var appModule: AppModule
//    }

    override fun onCreate() {
        super.onCreate()
//        appModule = AppModuleImpl(this)
        
        startKoin {
            androidLogger()  
            androidContext(this@MyApp)
            modules(appModule)
        }
    }
}

MainActivity.kt (修改)

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {

//        val sharedPref = getSharedPreferences("mypref" , Context.MODE_PRIVATE)
//        sharedPref.edit {
//            putString("username", "Mary")
//        }
//        println(sharedPref.getString("username", ""))
//        val username = sharedPref.getString("username", "") ?: ""

//        val viewModel by viewModels<MainViewModel> {
//            MainViewModelFactory(
//                MyApp.appModule.sharedPref
//            )
//        }


        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {

            DILabTheme {
                // 只需要這行
                val viewModel = getViewModel<MainViewModel>()
                // val viewModel:MainViewModel = koinViewModel()

                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        name = viewModel.username,
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }
}

這樣,就完成以 Koin 幫助我們完成依賴注入,執行後,結果如下:

問題

1、single() 與 singleOf() 有什麼不同

singleOf() 是 Koin 3.2 版後新增的功能,如果在一個依賴中,我們需要另一個之前建立過的依賴,我們會用 get()

val appModule = module {
    single<FirstDepClass> {
        FirstClass
    }
    
    // 如果 SecondDepClass 需要 FirstDepClass 的實例做為參數傳入,我們會用 get()
    single<SecondDepClass> {
        SecondDepClass(get())
    }
}

使用 singleOf() 的方式如下:

val appModule = module {
    single<FirstDepClass> {
        FirstClass
    }
    
    singleOf(::SecondDepClass)
}

得到的結果跟使用 single 相同。

參考資料

Koin Official Site

Koin 的那一兩件事情

Day22 – 依賴注入框架Koin

Koin: Lightweight Dependency Injection Framework

Last modified: 2025 年 7 月 7 日

Author

Comments

Write a Reply or Comment

Your email address will not be published.