标签: android-viewmodel

从 ViewModel 执行网络操作

我正在使用 IO/17 中引入的 ViewModel。

我正在使用 Android 开发者页面上提供的以下指南。 https://developer.android.com/topic/libraries/architecture/viewmodel.html

以下是他们的示例代码。

public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
     if (users == null) {
         users = new MutableLiveData<List<Users>>();
         loadUsers();
     }
     return users;
 }

 private void loadUsers() {
    // do async operation to fetch users
 }
}
Run Code Online (Sandbox Code Playgroud)

我希望在“ loadUsers() ”方法中执行 Volley 请求。但我不能这样做,因为它需要一个“上下文”,如下所示

Volley.newRequestQueue(context).add(jsonObjectRequest);
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,

  1. 是否建议(或可能)在 ViewModel 内执行网络操作?
  2. 如果是(如果可能),该怎么做?

android android-volley android-viewmodel

5
推荐指数
1
解决办法
3265
查看次数

重写 ViewModelStore

是否可以提供一次自己的ViewModelStoreforViewModelProviders实现来代替默认的实现?

更准确地说,我有兴趣添加fun clear(vm: ViewModel)(或使用索引或类似的东西)功能,以便ViewModelStore我可以清除我选择的单个视图模型,而不仅仅是使用内置的ViewModelStore#clear

public final void clear() {
    for (ViewModel vm : mMap.values()) {
        vm.onCleared();
    }
    mMap.clear();
}
Run Code Online (Sandbox Code Playgroud)

这会清除所有视图模型。

java inheritance android android-viewmodel android-architecture-components

5
推荐指数
1
解决办法
1804
查看次数

奇怪的 AndroidViewModel LiveData 观察者行为

我说奇怪是因为我不明白有人可能会告诉我什么正在按预期工作。

我有一个带有 LiveData 成员的 AndroidViewModel,我在 MainActivity 中观察到它来切换一些代码功能。LiveData 对象在视图模型的构造函数中分配初始值。

理论上一切工作正常,除了观察者行为在安装后第一次启动应用程序和随后的应用程序启动之间发生变化。

在安装后第一次启动期间,观察者在我设置后立即被触发,而底层的 LiveData 对象没有被更改。

在应用程序的后续启动期间,观察者不会在设置后过早触发,而是仅在我更改应用程序中其他位置的值时触发,这是我期望发生的情况。

最初我认为观察者以某种方式从 LiveData 初始化中获得延迟触发,但如果这是真的,那么无论是安装后第一次运行还是后续启动,它都应该发生。

因此,为了让应用程序按预期运行,如果应用程序在安装后首次运行,我必须在观察者中使用哨兵,以防止它们在第一次触发期间运行。

有人可以解释为什么会发生这种情况,以及它是否是预期的功能(我不相信),请向我指出解释此问题的文档?

我感觉我又在黑Android了。

下面是人们总是要求的一些代码片段,从 LiveData 声明开始。

@NonNull
private final MutableLiveData<Boolean> consentRequired = new MutableLiveData<>();
Run Code Online (Sandbox Code Playgroud)

ViewModel 构造函数初始化

    setConsentRequired(false);
Run Code Online (Sandbox Code Playgroud)

ViewModel 获取器/设置器

@NonNull
public LiveData<Boolean> getConsentRequired()
{
    return consentRequired;
}
@NonNull
public void setConsentRequired(@NonNull Boolean consentRequired)
{
    this.consentRequired.setValue(consentRequired);
}
Run Code Online (Sandbox Code Playgroud)

观察者

    getViewModel().getConsentRequired().observe(this, item ->
    {
        if (sentryAllowsObserverToRun)
        {
            // Do the observer stuff here
        }
    }
Run Code Online (Sandbox Code Playgroud)

SentryAllowsObserverToRun 是我必须设置的布尔值,以表明这不是安装后首次启动应用程序的第一个触发器。

android android-livedata android-viewmodel

5
推荐指数
1
解决办法
1199
查看次数

创建我自己的 ViewModelStore 来控制 ViewModel 生命周期

谷歌示例中指出,要在片段之间进行通信,您可以ViewModel使用Activity. 这种方法的问题在于,它将ViewModel持续到被Activity摧毁为止。

在单个Activity应用程序中,这意味着 Activity 将充斥着ViewModel可能不再需要的 s。ViewModel如果这些状态没有被正确清除,你也会遇到状态问题。

因此,我研究了如何改变 的生命周期,ViewModel这样我就不必受Activity生命周期的束缚,但可以比 的生命周期更长Fragment。这对于屏幕的多步骤/事务流非常有用,其中在屏幕流过程中满足了需求。

所以基本上,我希望 ViewModel 的范围小于活动但比片段长。

为了实现这一目标,我创建了自己的ViewModelStore并在配置中持久化它,就像FragmentActivity持久化它自己的ViewModelStore. 然后在初始化视图模型时我将使用,

ViewModelProvider(myCustomViewModelStore, myFactory).get(SomeViewModelClass::class.java)

由于ViewModel的范围不属于我的自定义范围ViewModelStore,因此我可以轻松调用viewModelStore.clear()来控制 的生命周期ViewModel

我想知道这是否是一个好主意以及是否有人使用相同的想法。

提前致谢!

android android-viewmodel android-architecture-components

5
推荐指数
1
解决办法
1174
查看次数

当我重新输入片段时,Android MutableLiveData 不断发射

我在 Navigation 组件中使用共享 ViewModel 而不是为每个片段创建一个 ViewModel(主要是因为它更容易)但是现在当我重新输入片段并订阅该片段的 ViewModel 实时数据时出现问题,我得到最后状态也太。

这是 ViewModel 代码:

  val apiLessonData: MutableLiveData<String>> = MutableLiveData()
  fun getLessonsUserCreated() =
        apiCall(MyMaybeObserver(apiLessonData))
Run Code Online (Sandbox Code Playgroud)

在 MyMaybeObserver 中,我有这样的事情:

override fun onSuccess(t: T) {
    apiDataObserver.postValue(t)
}
Run Code Online (Sandbox Code Playgroud)

这就是我在片段中观察它的方式:

private val apiAddGoalData = Observer<String> { response ->
    showSnack(response)
}

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        .
        .
        viewModel.apiAddGoalData.observe(viewLifecycleOwner, apiAddGoalData)
        .
        .
    }
Run Code Online (Sandbox Code Playgroud)

现在当我第一次进入它工作正常但我第二次打开它时,它显示了一次的小吃,如何在不创建新的 ViewModel 的情况下停止它?

android android-fragments android-livedata android-viewmodel

5
推荐指数
1
解决办法
954
查看次数

如何使用 navGraph 范围初始化 viewModel

我开始学习共享视图模型。目前我在活动中有 3 个片段,其中 2 个在嵌套的 navGraph 中。

我想为它们创建共享的 navGraph viewModel 范围,但我无法理解如何以及在哪里可以在这些片段中初始化视图模型。

在我过去的所有应用程序中,我创建了全局视图模型

private lateinit var viewModel: MainViewModel
Run Code Online (Sandbox Code Playgroud)

然后在里面onCreateView我像这样初始化 viewModel -

viewModel = ViewModelProvider(this, Factory(requireActivity().application)).get(
   MainViewModel::class.java)
Run Code Online (Sandbox Code Playgroud)

如果我想与 2 个片段共享一个视图模型,我如何对 navGraph viewModel 范围执行相同的操作?

目前我有这种方法:

private val homeViewModel: HomeViewModel by navGraphViewModels(R.id.nested_navigation)
Run Code Online (Sandbox Code Playgroud)

这是工作,但是

A.我从来没有在全局变量中看到过 viewModel

B.我不能用这种方法在工厂内部传递变量

android mvvm kotlin android-viewmodel android-jetpack

5
推荐指数
1
解决办法
1033
查看次数

如何对返回实时数据的函数进行单元测试

在我的 viewModel 中,我有一个返回 liveData 的函数。这个函数在片段中被直接调用,因此它在那里被直接观察到。我不知道如何测试这个函数,因为在测试的情况下没有观察到函数发出的 liveData,因此它不会返回值。

这是我的功能,我想为以下内容编写测试:

    fun saveRating(rating: Float, eventName: String): LiveData<Response<SaveRatingData?>?> {
        val request = RatingRequest(rating.toDouble(), eventName, false)

        return liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
            emit(repository.saveRatings(request))
        }

    }
Run Code Online (Sandbox Code Playgroud)

这就是我在片段中调用它的方式:

   viewModel.saveRating(rating, npsEventData?.eventName ?: "").observe(this, Observer {
      // on getting data
   })
Run Code Online (Sandbox Code Playgroud)

提前致谢!

android unit-testing android-livedata android-viewmodel kotlin-coroutines

5
推荐指数
1
解决办法
3192
查看次数

Android ViewModel 和点击监听器

从 MVP 到 viewModels,当谈到在哪里放置一些代码时,我觉得我有点迷茫。一个示例是放置点击侦听器的位置。在 MVP 我会做这样的事情

myButton.setOnClickListener { presenter.onMyButtonClicked }

我应该对 ViewModel 做同样的事情吗?我不这么认为。因为这意味着我将视图模型视为演示者。

但是,另一方面,如果我在视图(活动或片段)中处理单击侦听器,则视图可能不会像它应该的那样愚蠢。

应该处理点击监听器的最合适的地方在哪里?

android onclick mvvm onclicklistener android-viewmodel

5
推荐指数
1
解决办法
2919
查看次数

当使用返回流的存储库对视图模型进行单元测试时,将其转换为实时数据时会发生错误

我需要一些关于在 android 中编写单元测试的帮助,这些测试与视图模型、实时数据和流机制以及调度有关。

首先,我正在编写单元测试,而不是仪器测试。

实际上,我正在为一个 android 应用程序创建一个单元测试,用于测试使用存储库从互联网获取一些数据的 ViewModel。

我使用的视图模型的代码是这样的:

class ViewModel(private var repository: Repository? = Repository()) :
  androidx.lifecycle.ViewModel() {

  val data: LiveData<Result<Item>> = repository!!.remoteData.asLiveData()
}
Run Code Online (Sandbox Code Playgroud)

单元测试代码如下:

import junit.framework.TestCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.junit.MockitoJUnitRunner

@ExperimentalCoroutinesApi
@RunWith(MockitoJUnitRunner::class)
class ViewModelTest : TestCase() {
    private val testDispatcher = TestCoroutineDispatcher()
    private lateinit var repository: Repository
    private lateinit var viewModel: ViewModel …
Run Code Online (Sandbox Code Playgroud)

unit-testing android-livedata android-viewmodel kotlin-flow android-unit-testing

5
推荐指数
1
解决办法
932
查看次数

Jetpack Compose 从视图模型展示小吃吧 - 单个现场活动

我正在构建一个 jetpack compose 应用程序,我希望我的视图模型告诉我的 compose 函数通过向它发送一个事件来显示一个小吃店。我已经阅读了多篇关于 Kotlin 单一实时事件案例的博客文章,我尝试使用 Compose 和 Kotlin Flow 来实现它。我设法从视图模型发送事件(我在日志中看到它),但我不知道如何在可组合函数中接收它。有人可以帮我弄清楚吗?这是我的实现。

class HomeViewModel() : ViewModel() {
    sealed class Event {
        object ShowSheet : Event()
        object HideSheet : Event()
        data class ShowSnackBar(val text: String) : Event()
    }

    private val eventChannel = Channel<Event>(Channel.BUFFERED)
    val eventsFlow: Flow<Event> = eventChannel.receiveAsFlow()

    fun showSnackbar() {
        Timber.d("Show snackbar button pressed")
        viewModelScope.launch {
            eventChannel.send(Event.ShowSnackBar("SnackBar"))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
@Composable
fun HomeScreen(
    viewModel: HomeViewModel,
) {
    val context = LocalContext.current

    val scaffoldState = rememberScaffoldState()
    val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) …
Run Code Online (Sandbox Code Playgroud)

events android kotlin android-viewmodel android-jetpack-compose

5
推荐指数
1
解决办法
1024
查看次数