标签: android-viewmodel

Android ViewModel 协程启动测试不等待

我正在尝试使用 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

7
推荐指数
1
解决办法
2160
查看次数

禁止注入 @HiltViewModel 类,因为它无法正确创建 ViewModel 实例

我试图将用 @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 中?- 或者是旧的警告已经在最新版本的库中修复。

android-viewmodel dagger-hilt

7
推荐指数
1
解决办法
3110
查看次数

我可以将 LiveData 设为静态吗?

我不知道这是不是一个愚蠢的问题。这可能会违背 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

6
推荐指数
1
解决办法
4341
查看次数

从AndroidViewModel扩展时如何使用ViewModelProvider.Factory

我想向我的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

6
推荐指数
1
解决办法
2253
查看次数

观察一个 MutableLiveData 列表

我在我的应用程序中使用实时数据,并且我有一个从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)

但歌曲不会在此更改后通知。

为什么会发生这种情况?

谢谢您的回答。

android kotlin android-livedata android-viewmodel

6
推荐指数
1
解决办法
1万
查看次数

使用应用程序创建 ViewModel

我试图将数据设置到一个文件中的 ViewModel 上,但我不希望在该文件中保存任何活动引用。

A 类 -> 将数据设置到 ViewModel 中的 LiveData 上 具有 Application 类引用,但不保存活动或片段

B 类 -> 从 ViewModel 中的 LiveData 读取数据 拥有对 Activity 的引用。

B 类可以随着 Activity 的配置更改而被销毁和重新创建。A类持久化在内存中并不断向LiveData设置数据

问题:ViewModelProviders.of(activity orfragment).get() ViewProviders需要activity或fragment实例。

android android-livedata android-viewmodel

6
推荐指数
1
解决办法
1万
查看次数

具有交互器/用例的 MVVM 架构

语境

所以,我一直在为几个项目使用 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)

android mvvm android-livedata android-viewmodel

6
推荐指数
1
解决办法
3387
查看次数

实时数据在更改片段时再次赋予旧价值

我正在跨多个片段使用来自共享 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

6
推荐指数
1
解决办法
6332
查看次数

如何处理不同viewModel之间的通信?

我有一个包含许多片段的活动。每个片段都有一个其生命周期范围内的视图模型,其中包含一些逻辑。宿主活动也有一个 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 的生命周期。我认为。 …

java android kotlin android-viewmodel

6
推荐指数
1
解决办法
2659
查看次数

Android SavedStateHandle 不保存在 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 有什么想法吗?

android android-viewmodel

6
推荐指数
1
解决办法
1960
查看次数