MutableLiveData 未发布正确的值 - Android

MutableLiveData is not posting the correct value - Android

我正在围绕膳食构建一个应用程序 API。首先将显示类别列表。选择类别后,将显示特定类别的餐点。用户可以将任何餐点添加到收藏夹。用户可以通过单击顶部的收藏夹图标来查看收藏夹列表。如果用户点击一顿饭,他就可以看到这顿饭菜谱的详细信息。我正在使用相同的片段和视图模型来显示基于类别的餐点以及最喜欢的餐点。我将 MVVM 与 MutableLiveData 一起使用。收藏夹图标在 activity 中,我继续替换 activity 中的片段。

如果我单击 CategoriesFragment 或 FilterByTypeFragment 中的收藏夹图标,所有添加到收藏夹的食物都会正确显示。但是,如果我在 RecipeDetailsFragment 中单击收藏夹图标,它只会显示该类别的所有餐点。这意味着,它就像在 RecipeDetailsFragment 上按回键一样工作。当我调试时,我看到当我单击收藏夹图标时在视图模型中调用了 fetchFavorites 方法。但是观察者最后得到的是所选类别的所有餐点的列表。 当我从 RecipeDetailsFragment 导航到收藏夹部分时,为什么我看不到收藏夹列表? 我发布下面的代码以供您理解:

    @HiltViewModel
    class FilterByCategoryViewModel @Inject constructor(
        val dataManager: AppDataManager,
        val networkHelper: NetworkHelper,
        val category: String?,
        val isFavorites: String = "N"
    ) : ViewModel() {
    
        val _meals = MutableLiveData<Resource<MealsResponse>>()
    
        init {
            if (isFavorites.equals("Y")) fetchFavorites()
            else fetchMealsByCategory(category)
        }
    
        fun fetchMealsByCategory(category: String?) {
            viewModelScope.launch {
                _meals.postValue(Resource.loading(null))
                if (networkHelper.isNetworkConnected()) {
                    launch(Dispatchers.IO) {
                        dataManager.getMealsByCategory(category!!).let {
                            if (it.isSuccessful) {
                                _meals.postValue(Resource.success(it.body()))
                            } else _meals.postValue(
                                Resource.error(
                                    it.errorBody().toString(),
                                    null
                                )
                            )
                        }
                    }
                } else _meals.postValue(Resource.error("No Internet Connection", null))
            }
        }
    
        fun fetchFavorites() {
            viewModelScope.launch {
                _meals.postValue(Resource.loading(null))
                if (networkHelper.isNetworkConnected()) {
                    launch(Dispatchers.IO) {
                        dataManager.getFavoriteMeals().let {
                            if (it.isSuccessful) {
                                                            println("Body: " + it.body().toString())
_meals.postValue(Resource.success(it.body()))
                                println(_meals.getValue().toString())
                            } else _meals.postValue(
                                Resource.error(
                                    it.errorBody().toString(),
                                    null
                                )
                            )
                        }
                    }
                } else _meals.postValue(Resource.error("No Internet Connection", null))
            }
        }
    
    
        fun onFavoriteClicked(meal: Meal) {
            viewModelScope.launch {
                val job = launch(Dispatchers.IO) {
                    val isFavorite = dataManager.isFavorite(meal)
                    val _meal = meal.copy(
                        isFavorite = when (isFavorite) {
                            1 -> 0
                            else -> 1
                        }
                    )
                    dataManager.setFavorite(_meal)
                }
                job.join()
                if (isFavorites.equals("Y"))
                    fetchFavorites()
                else
                    fetchMealsByCategory(category)
            }
        }
    }

片段:

@AndroidEntryPoint
class FilterByTypeFragment : Fragment(), MealAdapter.FavoriteClickListener {

    @Inject
    lateinit var dataManager: AppDataManager

    @Inject
    lateinit var networkHelper: NetworkHelper
    private lateinit var adapter: MealAdapter
    private lateinit var binding: FragmentCategoriesBinding
    private var category: String? = null
    private var isFavorites: String = "N"
    lateinit private var filterByCategoryViewModel: FilterByCategoryViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = DataBindingUtil.inflate<FragmentCategoriesBinding>(
            inflater,
            R.layout.fragment_categories, container, false
        )
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val controller: NavController = Navigation.findNavController(view!!)
        controller.popBackStack(R.id.recipeDetailFragment, true)
        if (arguments != null) {
            category = FilterByTypeFragmentArgs.fromBundle(requireArguments()).category
            isFavorites = FilterByTypeFragmentArgs.fromBundle(requireArguments()).isFavorites
        }
        setupUI()
    }

    private fun setupUI() {
        binding.recyclerView.layoutManager = GridLayoutManager(activity, 2)
        adapter = MealAdapter(arrayListOf(), this)
        binding.recyclerView.addItemDecoration(
            GridSpacingItemDecoration(true, 2, 20, true)
        )
        binding.recyclerView.adapter = adapter
    }

    override fun onResume() {
        super.onResume()

        val factory = FilterByTypeViewModelFactory(dataManager, networkHelper, category, isFavorites)
        filterByCategoryViewModel = ViewModelProvider(
            this,
            factory
        ).get(FilterByCategoryViewModel::class.java)
        setupObserver()
    }

    private fun setupObserver() {
        filterByCategoryViewModel._meals.observe(this, Observer {
            when (it.status) {
                Status.SUCCESS -> {
                    binding.progressBar.visibility = View.GONE
                    it.data?.let { users ->
                        renderList(users.meals)
                    }
                    binding.recyclerView.visibility = View.VISIBLE
                }
                Status.LOADING -> {
                    binding.progressBar.visibility = View.VISIBLE
                    binding.recyclerView.visibility = View.GONE
                }
                Status.ERROR -> {
                    //Handle Error
                    binding.progressBar.visibility = View.GONE
                    Toast.makeText(activity, it.message, Toast.LENGTH_LONG).show()
                }
            }
        })
    }

    private fun renderList(meals: List<Meal>) {
        println("Meals: " + meals.toString())
        adapter.clearData()
        adapter.addData(meals)
        adapter.notifyDataSetChanged()
    }

    override fun onFavoriteClick(meal: Meal) {
        filterByCategoryViewModel.onFavoriteClicked(meal)
    }
}

以正确答案的形式在评论中写下讨论:
事实证明,问题是您正在导航到一个已经存在于后台堆栈中的目的地,并且由于有已经有一个作用域为该片段的 ViewModel,您收到了相同的 viewModel 实例(它的构造函数中已经有一个 isFavorites 字段。

通过在导航之前从后退堆栈中删除旧的目的地来解决问题,以便我们获得一个新的 ViewModel 实例。

除此之外,我建议不要将此类字段(依赖于业务逻辑)作为视图模型的依赖项(在您的情况下 categoryisFavorites)。相反,您可以采用其他一些方法,例如:

  • categoryisFavorites 作为导航参数传递,并使用 SavedStateHandle
  • 在 ViewModel 中检索它们
  • 在您的 ViewModel 中将这些字段表示为 LiveData 或 Flow,并对这些值的变化做出反应。