我正在尝试使用 Kotlin(1.6.21) Coroutines(1.6.4) 和 Kotlin Flow 进行 ViewModel 测试。
遵循官方Kotlin 协程测试文档,但ViewModel 在测试完成之前不会等待/返回挂起函数的结果。已经浏览了 StackOverflow 的热门答案,并尝试了所有建议的解决方案,例如注入相同的内容CoroutineDispatcher,并传递相同的内容CoroutineScope,但到目前为止没有一个有效。所以在这里我发布当前的简单测试实现。必须发布测试用例中涉及的所有类代码以获得更好的想法。
ReferEarnDetailViewModel.kt:
注入 Usecase 和 CoroutineContextProvider 并使用 viewModelScope 和提供的调度程序调用 API。但是从测试用例调用callReferEarnDetails()后,它不会收集模拟用例方法发出的任何数据。已尝试使用直接 repo 方法调用,也没有 Kotlin 流程,但同样失败。
@HiltViewModel class
ReferEarnDetailViewModel @Inject constructor(
val appDatabase: AppDatabase?,
private val referEarnDetailsUseCase: ReferEarnDetailsUseCase,
private val coroutineContextProvider: CoroutineContextProvider) : BaseViewModel() {
fun callReferEarnDetails() {
setProgress(true)
viewModelScope.launch(coroutineContextProvider.default + handler) {
referEarnDetailsUseCase.execute(UrlUtils.getUrl(R.string.url_referral_detail))
.collect { referEarnDetail ->
parseReferEarnDetail(referEarnDetail)
}
}
}
private fun parseReferEarnDetail(referEarnDetail:
ResultState<CommonEntity.CommonResponse<ReferEarnDetailDomain>>) {
when (referEarnDetail) …Run Code Online (Sandbox Code Playgroud) android kotlin android-testing android-viewmodel kotlin-coroutines
我试图将用 @HiltViewModel 注释的 ViewModel 注入到 Fragment 中并收到以下错误:
Injection of an @HiltViewModel class is prohibited since it does not create a ViewModel instance correctly.
Access the ViewModel via the Android APIs (e.g. ViewModelProvider) instead.
Injected ViewModel: com.example.MyViewModel
Run Code Online (Sandbox Code Playgroud)
这是否意味着我不应该使用 Hilt 将 ViewModel 注入到 Fragment 中?- 或者是旧的警告已经在最新版本的库中修复。
我不知道这是不是一个愚蠢的问题。这可能会违背 LiveData/ViewModel 的目的。
我可以将 LiveData 设为静态吗?我的原因是我有一个来自更新信息的服务的侦听器。所以我需要一种从服务中“设置/更改”LiveData 的方法。
我曾经执行以下操作并且它有效:
1. 服务更改数据库
2. ViewModel 侦听数据库更改
3. 来自 liveData 更改的 UI 更新
我发现这种方式太慢了。为了提高性能,我想要这样的东西:
1. 服务直接更改类对象
2. ViewModel 侦听类对象更改
3. 来自 liveData 更改的 UI 更新
为了实现我想要的,我需要使 MutableLiveData 静态或使 ViewModel 类在 Activity 之间共享 ViewModel 的相同实例。
这是个好主意吗?
public class MyViewModel extends AndroidViewModel {
// Note: this MutableLiveData is static
private static MutableLiveData<MyModel> mutableLiveData;
public MyViewModel(@NonNull Application application) {
super(application);
}
LiveData<MyModel> getLiveDataList() {
if (mutableLiveData == null) {
mutableLiveData = new MutableLiveData<>();
loadDataFromDb();
}
return mutableLiveData; …Run Code Online (Sandbox Code Playgroud) android android-livedata android-viewmodel android-architecture-components
我想向我的ViewModel发送一个额外的参数,但这是从AndroidViewModel扩展的。如何将此参数添加到ViewModelFactory类?
视图模型
class ProjectViewModel(application: Application) : AndroidViewModel(application) {
// need a param for project id...
}
Run Code Online (Sandbox Code Playgroud)
ViewModelFactory
class ProjectViewModelFactory(val projectId: Int): ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
// need to send this...
return ProjectViewModel(projectId) as T
}
}
Run Code Online (Sandbox Code Playgroud)
注意:我注意到在文档中它说:AndroidViewModel 子类必须具有一个接受Application作为唯一参数的构造函数。
所以我不知道做我想做的事是否可行(或很好)。
android android-viewmodel android-architecture-components android-jetpack
我在我的应用程序中使用实时数据,并且我有一个从ViewModel.
在我的视图模型中,我有一个列表:
var songs: MutableLiveData<List<Song>> = MutableLiveData<List<Song>>()
Run Code Online (Sandbox Code Playgroud)
在我的视图模型中的一个函数中,我这样做:
songs.value?.find { it.id == song.id }.also {
when (song.isFavorite) {
true -> song.isFavorite = false
false -> song.isFavorite = true
}
}
Run Code Online (Sandbox Code Playgroud)
我将songs在 m 片段中的一个项目中更改一个布尔值,我观察这个列表,如下所示:
viewModel.songs.observe(this , Observer {
Log.d(TAG , "songs changed")
})
Run Code Online (Sandbox Code Playgroud)
但歌曲不会在此更改后通知。
为什么会发生这种情况?
谢谢您的回答。
我试图将数据设置到一个文件中的 ViewModel 上,但我不希望在该文件中保存任何活动引用。
A 类 -> 将数据设置到 ViewModel 中的 LiveData 上 具有 Application 类引用,但不保存活动或片段
B 类 -> 从 ViewModel 中的 LiveData 读取数据 拥有对 Activity 的引用。
B 类可以随着 Activity 的配置更改而被销毁和重新创建。A类持久化在内存中并不断向LiveData设置数据
问题:ViewModelProviders.of(activity orfragment).get() ViewProviders需要activity或fragment实例。
语境
所以,我一直在为几个项目使用 MVVM 架构。我仍在努力弄清楚并改进架构的工作方式。我总是使用 MVP 架构,使用常用的工具集,Dagger for DI,通常是多模块项目,Presenter 层被注入了一堆 Interactor/UseCases,每个 Interactor 被注入了不同的 Repositories 来执行后端 API 调用.
现在我已经进入 MVVM,我通过 ViewModel 更改了 Presenter 层,从 ViewModel 到 UI 层的通信是通过 LiveData 完成的,而不是使用 View 回调接口,等等。
看起来像这样:
class ProductDetailViewModel @inject constructor(
private val getProductsUseCase: GetProductsUseCase,
private val getUserInfoUseCase: GetUserInfoUseCase,
) : ViewModel(), GetProductsUseCase.Callback, GetUserInfoUseCase.Callback {
// Sealed class used to represent the state of the ViewModel
sealed class ProductDetailViewState {
data class UserInfoFetched(
val userInfo: UserInfo
) : ProductDetailViewState(),
data class ProductListFetched(
val products: List<Product>
) …Run Code Online (Sandbox Code Playgroud) 我正在跨多个片段使用来自共享 ViewModel 的实时数据。我有一个登录片段,它需要用户的电话号码和密码,然后用户按下登录按钮,我为此调用 API,现在如果登录失败,我将显示一个 toast“登录失败”,现在如果用户转到“ForgotPassword”屏幕,该屏幕也使用与“SignInFragment”相同的视图模型,然后从忘记密码屏幕按回,出现登录片段,但它再次显示 toast“登录失败”,但 API没有被调用,它从之前注册的观察者那里获取数据,那么有什么方法可以解决这个问题吗?
SignInFragment.kt
class SignInFragment : Fragment() {
private lateinit var binding: FragmentSignInBinding
//Shared view model across two fragments
private val onBoardViewModel by activityViewModels<OnBoardViewModel>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_sign_in,
container,
false
)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
onBoardViewModel.signInResponse.observe(viewLifecycleOwner) { response ->
//This is calling again after coming back from new fragment it.
showToast("Sign In Failed")
} …Run Code Online (Sandbox Code Playgroud) android kotlin android-livedata android-viewmodel android-jetpack
我有一个包含许多片段的活动。每个片段都有一个其生命周期范围内的视图模型,其中包含一些逻辑。宿主活动也有一个 viewModel,包括一些显示弹出式消息的代码。
我希望我的片段视图模型能够向此弹出窗口发布消息。但是,我如何从我的片段视图模型之一访问活动视图模型?
我将编写一些示例性的 Kotlin 代码,但问题并不是 Kotlin 特有的,因为它更多的是一个架构问题。
class MyActivityViewModel {
...
popupMessage = MutableLiveData<String>("") // is observed by my activity
fun postMessage(text: String) {
popupMessage.value = text
}
}
class MyFragmentAViewModel {
...
fun someFunctionA() {
// want to call ActivityViewModel's postMessage from here
}
}
class MyFragmentBViewModel {
...
fun someFunctionB() {
// want to call ActivityViewModel's postMessage from here too
}
}
Run Code Online (Sandbox Code Playgroud)
我无法轻松调用,ViewModelProvider因为我不想在视图模型中保留对 Activity 的引用。我看到的唯一直接选项是通过构造函数或方法将 Activity-viewModel 传递给fragment-viewModel init()。这应该是安全的,因为父 viewModel 的生命周期应该超过片段 viewModel 的生命周期。我认为。 …
我有一个带有SavedStateHandle参数的 ViewModel。我在那里保存一个字符串,如下所示:
private fun saveString(str: String) {
state.set(KEY_STRING, str)
}
Run Code Online (Sandbox Code Playgroud)
然后我强制关闭我的应用程序并重新启动它,并希望检索保存的字符串,如下所示:
fun getSavedString(): String? {
return state.get<String>(KEY_String)
}
Run Code Online (Sandbox Code Playgroud)
但是,它总是返回 null。关于如何正确使用 SavedStateHandle 有什么想法吗?