範例簡介
我們以使用 SharedPreferences 為例來示範在 ViewModel 中的依賴注入。
範例程式碼
未使用 DI 的程式碼
第一階段:在 MainActivity 中,直接取得 SharedPreferences 的實例
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", "") ?: ""
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DILabTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = username,
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
...
第二階段:抽出邏輯,放到 ViewModel ,並把 SharePreferences 做為建構子傳入
MainViewModel.kt (新增)
class MainViewModel(
private val sharedPref: SharedPreferences
): ViewModel() {
init {
sharedPref.edit {
putString("username", "Steve")
}
}
val username = sharedPref.getString("username", "") ?: ""
}
class MainViewModelFactory(private val sharedPref: SharedPreferences) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create( modelClass: Class<T> ): T {
if( modelClass.isAssignableFrom( MainViewModel::class.java ) ) {
@Suppress( "UNCHECKED_CAST" )
return MainViewModel( sharedPref ) as T
}
throw IllegalArgumentException( "Unknown ViewModel Class" )
}
}
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(
sharedPref
)
}
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DILabTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = viewModel.username,
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
...
在這個階段,其實我們就將 SharedPreferencs 的實例 (也就是依賴,因為 MainViewModel 的運作依賴 SharedPreferences 實例) 注入到 MainViewModel 裏。
第三階段:抽出邏輯至 App 層級
為了提高程式碼的可重用性,我們再將 SharedPreferences 的實例拉高到 App 層級,讓其他會用到這個實例的程式,可以直接取用。
AppModule.kt (新增檔案)
interface AppModule {
val shreadPref: SharedPreferences
}
class AppModuleImpl(
private val appContext: Context
) {
override val sharedPref: SharedPreferences = appContext.getSharedPreferences("mypref", Context.MODE_PRIVATE)
}
MyApp.kt (新增檔案)
class MyApp: Application() {
companion object {
lateinit var appModule: AppModule
}
override fun onCreate() {
super.onCreate()
appModule = AppModuleImple(this)
}
}
AndroidMainfest.xml (編輯檔案)
...
<application
android:name=".MyApp"
...
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 {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = viewModel.username,
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
...
這樣就完成了手動將 SharedPreference 的依賴實例注入 MainViewModel 中的動作。
接下來,我們就可以在測試時,或是在不同狀況下,更換不同的 SharedPreference 實例,把測式的重心放在 MainViewModel 中,而不受 MainActivity 的影響。
而像 Koin 或 Dagger-Hilt 這一類的函式庫,只是提供了更便利的功能,讓我們在比較複雜的依賴注入時更加方便一些。
Comments