从官方得知
ViewModel 类旨在以生命周期意识的方式存储和管理 UI 相关数据
但我认为很多开发人员ViewModel同时用作数据存储和控制器(例如调用存储库、数据的网络客户端)。我还使用 as 作为数据存储和视图控制器。
Android官方示例代码也有一些控制器逻辑。来自官方:
class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData().also {
loadUsers()
}
}
fun getUsers(): LiveData<List<User>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
Run Code Online (Sandbox Code Playgroud)
这里loadUsers可能会调用一些Repository或NetworkClient。所以在这里它就像控制器一样。
我确信许多开发人员都这样做,但从定义上看ViewModel应该存储和管理 UI 相关数据,应该ViewModel充当控制器吗?
我发现了一些 stackoverflow 线程这个和这个关于这个。
第一个接受的答案建议不要用作ViewModel控制器并使用 …
android android-mvp android-viewmodel android-architecture-components
该视频(MVVM 和嵌套片段/视图:ViewModel 合约 - Marcos Paulo Damesceno、Bret Erickson droidcon 旧金山 2019)展示了一种使用ViewModel.
我出于学习目的而实施它,但我陷入了困境。
// 18:35 of the video
private const val VM_KEY = "view_model_contract_key"
fun <T> Fragment.viewModelContracts() = lazy {
val clazz: Class<ViewModel> = arguments?.getSerializable(VM_KEY) as Class<ViewModel>
val viewModelProvider = ViewModelProvider(requireActivity())
return@lazy viewModelProvider.get(clazz) as T
}
Run Code Online (Sandbox Code Playgroud)
传递的参数ViewModelStoreOwner是 an Activity,但是如果我有一个Fragment内部的另一个对象Fragment,并且它们共享相同的对象ViewModel,则ViewModel返回的对象viewModelContracts()将是与 Parent 创建的对象不同的对象Fragment。
interface ChildViewModelContract {
// ...
}
class SomeViewModel : ViewModel(), ChildViewModelContract { …Run Code Online (Sandbox Code Playgroud) 我有一个视图模型。它调用我的数据存储库中的函数并返回狗对象的列表。
class MainViewModel() : ViewModel() {
private val dataRepo: DataRepo by inject(DataRepo::class.java) //dataRepo
private var limit = 10
private val _dogListLiveData = MutableLiveData<List<Dog>>()
private var dogList = mutableListOf<Dog>()
val dogListLiveData: MutableLiveData<List<Dog>>
get() = _dogListLiveData
fun searchByBreed(queryText: String) {
dataRepo.searchByBreed(
queryText,
object : DataSource.OnResponseCallback<List<Dog>, String> {
override fun onSuccess(obj: List<Dog>?) {
dogList = mutableListOf()
if(!obj.isNullOrEmpty()){
dogList.addAll(obj)
dogListLiveData.value = dogList.take(limit)
}
}
override fun onError(error: String) {
Log.i("Calling Network Service", error)
}
})
}
fun loadPaginateBreed() : Boolean{
return if ((limit+10) < …Run Code Online (Sandbox Code Playgroud) 谷歌表示,在 Fragment 之间进行通信时,通过将其范围限定为 Activity 来使用 SharedViewModel。
在单个 Activity 应用程序中,这意味着该 Activity 将散布着可能不再需要的 ViewModel,并且它们将在整个生命周期中保留在那里。考虑一些示例,例如扩展的注册流程或考虑几个屏幕的示例。
推荐的一种方法是使用父片段作为范围,但这只有在存在父片段时才可能。它可以只是不同的片段。
我想出了以下解决方案,我想知道它有多可行或有多糟糕,是否有更好的方法?
考虑到我有两个名为ExampleOneFragmentand的片段ExampleTwoFragment,为了简单起见,我希望它们具有共享范围,而不实际将其范围限定到活动。假设我想更新其中的文本视图ExampleOneFragment,ExampleTwoFragment因此我为两者创建一个像这样的 SharedViewModel
因为ExampleOneFragment它将是:
private val mSharedViewModel by lazy {
ViewModelProvider(this).get(SharedViewModel::class.java)
}
Run Code Online (Sandbox Code Playgroud)
因为ExampleTwoFragment我想出了这个:
private val mSharedViewModel by lazy {
ViewModelProvider(supportFragmentManager().findFragmentByTag(ExampleOneFragment.TAG) ?: this).get(SharedViewModel::class.java)
}
Run Code Online (Sandbox Code Playgroud)
这似乎有效,但我不知道这会导致什么样的问题。
我发现了一些其他解决方案:根据@mikhehc这里,我们实际上可以创建我们自己的 ViewModelStore。这将使我们能够精细地控制 ViewModel 必须存在的范围。但我不明白如何让它适用于片段?
其次,仍然是将其范围限定为活动的黑客方法,但使用我在此处找到的相同密钥通过虚拟视图模型将其清除
谁能指导我什么是正确的方法?我无法转向 NavGraphs,因为它是一个已经启动并正在运行的项目,并且确定活动范围感觉是错误的。谢谢。
android android-fragments android-navigation android-viewmodel android-architecture-navigation
是在 ViewModel 中启动协程更好,还是用suspend修饰符标记 ViewModel 函数并在 Activity/fragment 本身中启动协程更好?
在 ViewModel 中启动:
class MainViewModel: ViewModel() {
fun addNewItem(item: Item) {
viewModelScope.launch {
// Add the item to database
}
}
}
Run Code Online (Sandbox Code Playgroud)
class ItemsFragment: Fragment() {
fun onButtonClick() {
viewModel.addNewItem(Item())
}
}
Run Code Online (Sandbox Code Playgroud)
在 LifeCycleOwner 中启动:
class MainViewModel: ViewModel() {
suspend fun addNewItem(item: Item) {
// Add the item to database
}
}
Run Code Online (Sandbox Code Playgroud)
class ItemsFragment: Fragment() {
fun onButtonClick() {
lifecycleScope.launchWhenStarted {
viewModel.addNewItem(Item())
}
}
}
Run Code Online (Sandbox Code Playgroud) android kotlin android-viewmodel kotlin-coroutines coroutinescope
我有一个关于 Android 应用程序架构的一般性问题。我正在实现一个应用程序(用Java),它有一个活动和20个片段(相似但不相同)。到目前为止,我在片段中实现了所有内容(UI、逻辑、数据库查询)。现在我正在考虑使用 ViewModel,但我不确定它是否值得付出努力。所以我的问题是我的 20 个片段中的每一个片段是否都应该有一个自己的 ViewModel,或者我是否可以为所有 20 个片段实现一个 ViewModel?为所有 Fragment 类实现一个 ViewModel 会大大增加工作量,所以我想知道是否可以为所有 Fragment 只使用一个 ViewModel ?
我目前正在使用 Koin 版本 3.1.0 并想升级到最新版本(当前为 3.1.4),但是一旦我将 Koin 升级到 3.1.3 以上,我在运行测试时会出现以下异常:
class java.lang.Object cannot be cast to class androidx.lifecycle.SavedStateHandleController (java.lang.Object is in module java.base of loader 'bootstrap'; androidx.lifecycle.SavedStateHandleController is in unnamed module of loader org.robolectric.internal.AndroidSandbox$SdkSandboxClassLoader @5c2a3b10)
java.lang.ClassCastException: class java.lang.Object cannot be cast to class androidx.lifecycle.SavedStateHandleController (java.lang.Object is in module java.base of loader 'bootstrap'; androidx.lifecycle.SavedStateHandleController is in unnamed module of loader org.robolectric.internal.AndroidSandbox$SdkSandboxClassLoader @5c2a3b10)
at androidx.lifecycle.SavedStateHandleController.attachHandleIfNeeded(SavedStateHandleController.java:98)
at androidx.lifecycle.AbstractSavedStateViewModelFactory.onRequery(AbstractSavedStateViewModelFactory.java:103)
at androidx.lifecycle.StateViewModelFactory.onRequery(StateViewModelFactory.kt:37)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:160)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:139)
at org.koin.androidx.viewmodel.ViewModelResolverKt.resolveInstance(ViewModelResolver.kt:15)
at org.koin.androidx.viewmodel.scope.ScopeExtKt.getViewModel(ScopeExt.kt:67)
at org.koin.androidx.viewmodel.scope.ScopeExtKt.getViewModel(ScopeExt.kt:53)
at org.koin.androidx.viewmodel.scope.ScopeExtKt.getViewModel$default(ScopeExt.kt:45)
at …Run Code Online (Sandbox Code Playgroud) android classcastexception robolectric android-viewmodel koin
我目前正在开发一个项目来练习使用 Dagger-Hilt 进行依赖注入,但是我无法实例化我的 ViewModel,我已经查看了同一主题的其他一些问题,大多数问题都指向 Gradle,但是没有为我修复它,Stacktrace 中的所有错误都来自内部类,除了 MainActivity 中涉及我的 ViewModel 的两行之外
\n这是堆栈跟踪:
\nE/AndroidRuntime: FATAL EXCEPTION: main\nProcess: com.example.practiceproject, PID: 9873\njava.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.practiceproject/com.example.practiceproject.ui.MainActivity}: java.lang.RuntimeException: Cannot create an instance of class com.example.practiceproject.ui.viewmodel.DogViewModel\n at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3270)\n at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)\n at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)\n at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)\n at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)\n at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)\n at android.os.Handler.dispatchMessage(Handler.java:107)\n at android.os.Looper.loop(Looper.java:214)\n at android.app.ActivityThread.main(ActivityThread.java:7356)\n at java.lang.reflect.Method.invoke(Native Method)\n at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)\n at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)\n Caused by: java.lang.RuntimeException: Cannot create an instance of class com.example.practiceproject.ui.viewmodel.DogViewModel\n at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:221)\n at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:278)\n at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:112)\n at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:146)\n at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:111)\n at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:187)\n …Run Code Online (Sandbox Code Playgroud) 我有一个 Fragment0,其中包含一个 ViewPager2,该 ViewPager2 内部可能包含 1 个或多个子片段。
我创建了一个 SharedViewModel 并在 Parent 片段中定义它,如下所示:
val viewModel by viewModels<SharedViewModel>()
Run Code Online (Sandbox Code Playgroud)
我还在每个子片段中添加了 viewModel 的定义。
private val sharedViewModel by viewModels<SharedViewModel>(
ownerProducer = { requireParentFragment() }
)
Run Code Online (Sandbox Code Playgroud)
我使用 NavigationControl 从每个子片段即导航到 Fragment4。片段 1、片段 2 和片段 3。我的问题是,如何与 Fragment4 共享相同的视图模型,它不是 Fragment0 的直接子级。我可以使用相同的方法与 Fragment4 共享 viewModel 吗?
或者有更好的方法来处理这样的用例吗?
android viewmodel android-fragments kotlin android-viewmodel
我的应用程序使用 hilt,我LoadManager在活动内部进行了一些工作,使用读取联系人ContentResolver,当我完成工作时,我得到发送到我的 viewModel 的光标,以便处理数据并执行一些业务逻辑,为此我声明了以下内容在我的活动之上:
@AndroidEntryPoint
class MainActivity : ComponentActivity(), LoaderManager.LoaderCallbacks<Cursor> {
private val contactsViewModel: ContactsViewModel by viewModels()
...
Run Code Online (Sandbox Code Playgroud)
这样我就可以在里面使用它onLoadFinished :
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor?) {
contactsViewModel.updateContactsListFromCursor(cursor, loader.id)
}
Run Code Online (Sandbox Code Playgroud)
在我的 viewModel 中,我有以下代码,它使用要显示的联系人更新列表的 ui 状态:
data class ContactsListUiState(
val contacts: MutableList<Contact>,
val searchFilter: String)
@HiltViewModel
class ContactsViewModel @Inject constructor() : ViewModel() {
private val _contactsListUiState =
MutableStateFlow(ContactsListUiState(mutableStateListOf(), ""))
val contactsListUiState: StateFlow<ContactsListUiState> = _contactsListUiState.asStateFlow()
private fun updateContactsList(filter: String) {
viewModelScope.launch(Dispatchers.IO) {
...
_contactsListUiState.update { currentState -> …Run Code Online (Sandbox Code Playgroud) android android-viewmodel android-jetpack-compose dagger-hilt jetpack-compose-navigation
android ×10
kotlin ×4
koin ×2
viewmodel ×2
android-architecture-components ×1
android-architecture-navigation ×1
android-mvp ×1
dagger-hilt ×1
java ×1
robolectric ×1