建立含依附元件的 ViewModel Android Jetpack 的一部分。
遵循依附元件插入最佳做法,ViewModel 可將依附元件做為其建構函式中的參數。這些類型主要來自網域或資料層。由於架構提供 ViewModel,因此必須採用特殊的機制來建立其執行個體。這種機制即為 ViewModelProvider.Factory
介面。只有實作這個介面才能在適當範圍內將 ViewModel 執行個體化。
ViewModels 搭配 CreationExtras
如果 ViewModel
類別在其建構函式中接收依附元件,請提供實作 ViewModelProvider.Factory
介面的工廠。覆寫 create(Class<T>, CreationExtras)
函式以提供 ViewModel 的新執行個體。
CreationExtras
允許您存取有助於對 ViewModel 執行個體化的相關資訊。下面是可透過額外項目存取的金鑰清單:
金鑰 | 功能 |
---|---|
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY |
提供傳遞至 ViewModelProvider.get() 的自訂金鑰存取權。 |
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY |
提供 Application 類別的執行個體存取權。 |
SavedStateHandleSupport.DEFAULT_ARGS_KEY |
提供應當用於建構 SavedStateHandle 的引數套件存取權。 |
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY |
提供用於建構 ViewModel 的 SavedStateRegistryOwner 存取權。 |
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY |
提供用於建構 ViewModel 的 ViewModelStoreOwner 存取權。 |
如要建立新的 SavedStateHandle
例項,請使用 CreationExtras.createSavedStateHandle()
函式,並將其傳遞至 ViewModel。
使用 APPLICATION_KEY 的 CreationExtras
以下範例說明如何提供 ViewModel
的例項,該例項以範圍限定為 Application
類別的存放區做為依附元件:SavedStateHandle
Kotlin
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.CreationExtras
class MyViewModel(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// ViewModel logic
// ...
// Define ViewModel factory in a companion object
companion object {
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
extras: CreationExtras
): T {
// Get the Application object from extras
val application = checkNotNull(extras[APPLICATION_KEY])
// Create a SavedStateHandle for this ViewModel from extras
val savedStateHandle = extras.createSavedStateHandle()
return MyViewModel(
(application as MyApplication).myRepository,
savedStateHandle
) as T
}
}
}
}
Java
import static androidx.lifecycle.SavedStateHandleSupport.createSavedStateHandle;
import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;
import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.viewmodel.ViewModelInitializer;
public class MyViewModel extends ViewModel {
public MyViewModel(
MyRepository myRepository,
SavedStateHandle savedStateHandle
) { /* Init ViewModel here */ }
static final ViewModelInitializer<MyViewModel> initializer = new ViewModelInitializer<>(
MyViewModel.class,
creationExtras -> {
MyApplication app = (MyApplication) creationExtras.get(APPLICATION_KEY);
assert app != null;
SavedStateHandle savedStateHandle = createSavedStateHandle(creationExtras);
return new MyViewModel(app.getMyRepository(), savedStateHandle);
}
);
}
之後您就可以在擷取 ViewModel 執行個體時使用這個工廠:
Kotlin
import androidx.activity.viewModels
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels { MyViewModel.Factory }
// Rest of Activity code
}
Java
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
public class MyActivity extends AppCompatActivity {
MyViewModel myViewModel = new ViewModelProvider(
this,
ViewModelProvider.Factory.from(MyViewModel.initializer)
).get(MyViewModel.class);
// Rest of Activity code
}
Jetpack Compose
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
modifier: Modifier = Modifier,
viewModel: MyViewModel = viewModel(factory = MyViewModel.Factory)
) {
// ...
}
或者,您也可以使用 ViewModel
工廠 DSL,以更符合語言習慣的 Kotlin API 建立工廠:
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
class MyViewModel(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// ViewModel logic
// Define ViewModel factory in a companion object
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val savedStateHandle = createSavedStateHandle()
val myRepository = (this[APPLICATION_KEY] as MyApplication).myRepository
MyViewModel(
myRepository = myRepository,
savedStateHandle = savedStateHandle
)
}
}
}
}
將自訂參數傳遞為 CreationExtras
您可以建立自訂索引鍵,透過 CreationExtras
將依附元件傳遞至 ViewModel
。如果 ViewModel
依附於無法透過 Application
類別和 APPLICATION_KEY
存取的物件,這項功能就能派上用場。舉例來說,如果 ViewModel
是在 Kotlin 多平台模組中建立,就無法存取 Android 依附元件。
在此範例中,ViewModel
定義了自訂鍵,並用於 ViewModelProvider.Factory
中。
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
class MyViewModel(
private val myRepository: MyRepository,
) : ViewModel() {
// ViewModel logic
// Define ViewModel factory in a companion object
companion object {
// Define a custom key for your dependency
val MY_REPOSITORY_KEY = object : CreationExtras.Key<MyRepository> {}
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
// Get the dependency in your factory
val myRepository = this[MY_REPOSITORY_KEY] as MyRepository
MyViewModel(
myRepository = myRepository,
)
}
}
}
}
您可以使用 CreationExtras.Key
從 ViewModelStoreOwner
(例如 ComponentActivity
、Fragment
或 NavBackStackEntry
) 例項化 ViewModel
,也可以使用 Jetpack Compose 進行例項化。
Kotlin
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.MutableCreationExtras
// ...
// Use from ComponentActivity, Fragment, NavBackStackEntry,
// or another ViewModelStoreOwner.
val viewModelStoreOwner: ViewModelStoreOwner = this
val myViewModel: MyViewModel = ViewModelProvider.create(
viewModelStoreOwner,
factory = MyViewModel.Factory,
extras = MutableCreationExtras().apply {
set(MyViewModel.MY_REPOSITORY_KEY, myRepository)
},
)[MyViewModel::class]
Jetpack Compose
import androidx.lifecycle.viewmodel.MutableCreationExtras
import androidx.lifecycle.viewmodel.compose.viewModel
// ...
@Composable
fun MyApp(myRepository: MyRepository) {
val extras = MutableCreationExtras().apply {
set(MyViewModel.MY_REPOSITORY_KEY, myRepository)
}
val viewModel: MyViewModel = viewModel(
factory = MyViewModel.Factory,
extras = extras,
)
}
ViewModel 2.5.0 以下版本的工廠
如果您使用的是 2.5.0 以下版本的 ViewModel
,則必須從擴充 ViewModelProvider.Factory
並實作 create(Class<T>)
函式的類別子集中提供工廠。根據 ViewModel
所需的依附元件,必須從中延伸不同類別:
AndroidViewModelFactory
(如果需要Application
類別)。AbstractSavedStateViewModelFactory
(如果需要做為依附元件傳遞SavedStateHandle
)。
如果不需要 Application
或 SavedStateHandle
,則從 ViewModelProvider.Factory
擴充即可。
下列範例使用 ViewModel 的 AbstractSavedStateViewModelFactory
,並將存放區和 SavedStateHandle
類型做為依附元件:
Kotlin
class MyViewModel(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
// ViewModel logic ...
// Define ViewModel factory in a companion object
companion object {
fun provideFactory(
myRepository: MyRepository,
owner: SavedStateRegistryOwner,
defaultArgs: Bundle? = null,
): AbstractSavedStateViewModelFactory =
object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
return MyViewModel(myRepository, handle) as T
}
}
}
}
Java
import androidx.annotation.NonNull;
import androidx.lifecycle.AbstractSavedStateViewModelFactory;
import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;
public class MyViewModel extends ViewModel {
public MyViewModel(
MyRepository myRepository,
SavedStateHandle savedStateHandle
) { /* Init ViewModel here */ }
}
public class MyViewModelFactory extends AbstractSavedStateViewModelFactory {
private final MyRepository myRepository;
public MyViewModelFactory(
MyRepository myRepository
) {
this.myRepository = myRepository;
}
@SuppressWarnings("unchecked")
@NonNull
@Override
protected <T extends ViewModel> T create(
@NonNull String key, @NonNull Class<T> modelClass, @NonNull SavedStateHandle handle
) {
return (T) new MyViewModel(myRepository, handle);
}
}
然後使用工廠來擷取 ViewModel:
Kotlin
import androidx.activity.viewModels
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels {
MyViewModel.provideFactory((application as MyApplication).myRepository, this)
}
// Rest of Activity code
}
Java
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
public class MyActivity extends AppCompatActivity {
MyViewModel myViewModel = new ViewModelProvider(
this,
ViewModelProvider.Factory.from(MyViewModel.initializer)
).get(MyViewModel.class);
// Rest of Activity code
}
Jetpack Compose
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
modifier: Modifier = Modifier,
viewModel: MyViewModel = viewModel(
factory = MyViewModel.provideFactory(
(LocalContext.current.applicationContext as MyApplication).myRepository,
owner = LocalSavedStateRegistryOwner.current
)
)
) {
// ...
}
為您推薦
- 注意:系統會在 JavaScript 關閉時顯示連結文字
- ViewModel 的已儲存狀態模組
- 儲存 UI 狀態
- LiveData 總覽