标签: android-viewmodel

Android 架构组件和 LiveData,太多 MutableLiveData 对象?

在发布 android 架构组件之前,我开始从事一个项目,在该项目中,我拥有自己的 ViewModel,该 ViewModel 与 Fragment 具有相同的生命周期,并且 ViewModels 状态保存在 Loader 内的 StateObject 中,以便状态可以在方向更改后幸免于难。ViewModel 通过接口与 Fragment 通信。这很有效,因为 ViewModel 和 Fragment 具有相同的生命周期。我的 ViewModel 包含各种状态。它有一个 isLoading 布尔值、isEmptyStateVisible 布尔值等等。每次状态改变时,我都会调用类似 view.notifyIsLoadingChanged(true/false) 的东西,在这种情况下,Fragment 将显示或隐藏一个微调器。

现在我将更改我的实现以将新的 ViewModel 与 LiveData 一起使用。实现 LiveData 的最快方法是更改​​我对 ViewModel 正在使用的 UI 界面的实现。所以我可以保留我当前的实现,只添加这个 UI 界面实现:

public class LiveDataProductReviewSheetUI extends LiveDataUI implements ProductReviewSheetUI {
    public final MutableLiveData<ReviewViewModelState> ratingDescChanged = new MutableLiveData<>();
    public final MutableLiveData<ReviewViewModelState> ratingChanged = new MutableLiveData<>();
    public final MutableLiveData<ReviewViewModelState> reviewChanged = new MutableLiveData<>();
    public final MutableLiveData<ReviewViewModelState> reviewValid = new MutableLiveData<>();
    public final …
Run Code Online (Sandbox Code Playgroud)

android android-livedata android-viewmodel android-architecture-components

6
推荐指数
0
解决办法
1465
查看次数

androidx 数据绑定与 Spinner 和自定义对象

您如何使用 androidx 数据绑定库用自定义对象列表(app:entries)填充 Spinner ?以及如何为 Spinner (app:onItemSelected)创建正确的选择回调?

我的布局:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>

    <variable
        name="viewModel"
        type=".ui.editentry.EditEntryViewModel" />
</data>

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.editentry.EditEntryActivity">

        <Spinner
            android:id="@+id/spClubs"
            android:layout_width="368dp"
            android:layout_height="25dp"
            app:entries="@{viewModel.projects}"
            app:onItemSelected="@{viewModel.selectedProject}"
             />

</FrameLayout>

</layout>
Run Code Online (Sandbox Code Playgroud)

编辑EntryViewModel.kt

class EditEntryViewModel(repository: Repository) : ViewModel() {

    /** BIND SPINNER DATA TO THESE PROJECTS **/
    val projects : List<Project> = repository.getProjects()

    /** BIND SELECTED PROJECT TO THIS VARIABLE **/
    val selectedProject: Project;
}
Run Code Online (Sandbox Code Playgroud)

项目.kt

data class Project(
    var id: Int? = null,
    var name: String = …
Run Code Online (Sandbox Code Playgroud)

android android-spinner android-databinding android-viewmodel

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

观察一个 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万
查看次数

带有 CoroutineScope 的边界回调(Android 分页库)

我试图实现Android Paging libraryViewModelKotlin Coroutines

我有一个ViewModel实现CoroutineScope. 这取决于Repository

class MovieListViewModel(
    private val movieRepository: MovieRepository
) : ViewModel(), CoroutineScope {

    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.IO

    private lateinit var _movies: LiveData<PagedList<MovieBrief>>

    val movies: LiveData<PagedList<MovieBrief>>
        get() = _movies

    init {
        launch {
            _movies = LivePagedListBuilder(movieRepository.getMovies(), DATABASE_PER_PAGE)
                .setBoundaryCallback(movieRepository.movieBoundaryCallback)
                .build()
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

这是我的Repository. 我注入BoundaryCallbackKodeinDependecy注射器。

class MovieRepositoryImpl(
    private val movieDao: MovieDao, …
Run Code Online (Sandbox Code Playgroud)

android kotlin android-viewmodel kotlinx.coroutines android-paging

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

具有交互器/用例的 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
查看次数

从 LiveData 获取一个值

我有LiveDataBooks inViewModel的构造函数:

LiveData<List<Book>> books;

public MyViewModel(@NonNull Application application) {
    super(application);
    books = bookRepository.getBooks();
}
Run Code Online (Sandbox Code Playgroud)

当用户book从 UI创建新书时,我希望属性book_order填充最多增加book_order的其他书籍。为了更好地描述我想要什么,请参阅以下 preudocode:

book.book_order = max(books.book_order) + 1;
Run Code Online (Sandbox Code Playgroud)

因此,当有三本书book_order分别为 1、2、3 时,新书会将这个属性设置为 4。

问题是,我怎么能用LiveDatain做到这一点ViewModel?我尝试使用Transformations.map新的LiveData,但这种方法根本不起作用,bookMax似乎是null.

public void insertBook(Book book) {
    LiveData<Integer> bookMax = Transformations.map(books,  
        list -> {
          int value = 0;
          for(Book b: list) {
              if (value < b.getBookOrder()) {
                 value = b.getBookOrder();
              } …
Run Code Online (Sandbox Code Playgroud)

android android-room android-livedata android-viewmodel

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

ViewModel 没有在导航中被清除,并且视图模型中的实时数据保持活动状态

因此,我使用导航实现了具有多个片段模式的单个活动。我为非 ui 操作的每个片段使用了视图模型。

问题是当您使用 导航时findNavController().navigate(),片段实际上并未被销毁。只有onDestroyView被调用。因此,片段的 onDestroy 永远不会被调用,随后视图模型也不会被清除,因此 LiveData 观察者也保持活动状态,当我回到片段时,观察者再次被创建,因此实时数据被观察到两次。一次使用它保存的旧数据,第二次使用来自某些操作的新数据。

例如,我有片段A和片段B

A显示一个列表,B您可以添加将显示在列表中的内容。也许从片段B 中的api 获取新数据以显示在A 中

因此,当我从片段B返回到A 时,观察者首先使用旧数据调用两次,然后使用更新数据调用第二次。最后,列表显示了正确的数据,但我不希望出现两个观察者。

我已经关注了这篇文章 https://medium.com/@BladeCoder/architecture-components-pitfalls-part-1-9300dd969808

并尝试使用viewLifeCycleOwner代替,this但这无济于事,问题仍然存在。

我还尝试在观察之前删除观察者:

vm.ld.removeObservers(this)
vm.ld.observe(viewLifeCyclerOwner, observer)
Run Code Online (Sandbox Code Playgroud)

问题仍然存在。

(我也尝试删除观察者onDestroyView,但问题仍然存在。)

唯一的周围,我发现工作是手动调用视图模型onClearedonDestroyView,并清除livedata。

在片段 onDestroyView

vm.clear()
Run Code Online (Sandbox Code Playgroud)

在视图模型中

fun clear() = onCleared()

override fun onCleared() {
  //do stuff
}
Run Code Online (Sandbox Code Playgroud)

现在,这解决了我的问题。但我觉得这不是一个可靠的解决方案,可以有更好的方法来做到这一点。如果有人能对此有所了解,我会很高兴。谢谢。

navigation android android-fragments android-livedata android-viewmodel

6
推荐指数
2
解决办法
1013
查看次数

ViewModelProviders 在 2.2.0 中被弃用

目前我androidx.lifecycle:lifecycle-extensions2.2.0-alpha01to更新版本2.2.0,它显示 ViewModelProviders 已被弃用。那么在 kotlin 中使用ViewModelProviders的替代方法是什么?

kotlin android-viewmodel android-jetpack viewmodelproviders

6
推荐指数
3
解决办法
4936
查看次数

在从暂停的存储库中获取的 ViewModel 中传递 LiveData 的正确方法

我正在考虑 Kotlin 协程和 LiveData。我想做一个非常基本的用例,其中 ViewModel 返回从 Repository 挂起函数中获取的 LiveData,该函数也返回 LiveData。

存储库函数签名:

suspend fun getAll() : LiveData<List<Mountain>> 
Run Code Online (Sandbox Code Playgroud)

它不能简单地做到这一点:

fun getMountains() : LiveData<List<Mountain>> {
  return mountainsRepository.getAll()
}
Run Code Online (Sandbox Code Playgroud)

因为编译器声明挂起函数应该从协程或另一个挂起函数调用。我想出了两个丑陋的解决方案,但我知道它们并不优雅:

1 使用 runBlocking 的解决方案

fun getMountains() : LiveData<List<Mountain>> = runBlocking { mountainsRepository.getAll() }
Run Code Online (Sandbox Code Playgroud)

2 具有可为空 LiveData 的解决方案

fun getMountains() : LiveData<List<Mountain>>?{
    var mountains : LiveData<List<Mountain>>? = null
    viewModelScope.launch{
        mountains = mountainsRepository.getAll()
    }
    return mountains
}
Run Code Online (Sandbox Code Playgroud)

我怎样才能正确地做到这一点?

kotlin android-livedata android-viewmodel kotlin-coroutines

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

观察 ViewModel 初始化中的 StateFlow 发射

我有一个视图模型,它接受初始ViewState对象并具有state可以收集的公共访问变量。

class MyViewModel<ViewState>(initialState: ViewState) : ViewModel() {
    val state: StateFlow<ViewState> = MutableStateFlow(initialState)
    val errorFlow: SharedFlow<String> = MutableSharedFlow()

    init {
        performNetworkCall()
    }

    private fun performNetworkCall() = viewModelScope.launch {
        Network.makeCall(
            "/someEndpoint",
            onSuccess = {
                (state as MutableStateFlow).tryEmit(<some new state>)
            },
            onError = {
                (errorFlow as MutableSharedFlow).tryEmit("network failure")
            }
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

从片段观察此状态时,我可以看到初始状态(例如正在加载),并在网络调用成功完成时收集更改(例如,到已加载状态)。

然而,我不知道如何观察我的发射ViewModelUnitTest。我使用 kotlin 涡轮机来测试我的状态和共享流的排放,但我只能观察在调用viewModel.state.test或后发生的排放viewModel.errorFlow.test

由于我无法引用viewModel.stateviewModel.errorFlow在初始化 ViewModel 之前,如何编写测试来验证我的初始化逻辑是否正确执行并根据模拟行为发出预期结果Network.makeCall- 无论是新的发射state还是errorFlow发射?

android kotlin android-viewmodel kotlin-coroutines kotlin-flow

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