【问题标题】:Android Fragment and ViewModel issueAndroid Fragment 和 ViewModel 问题
【发布时间】:2020-08-23 07:24:23
【问题描述】:

我已经创建了 FragmentA 并在 onViewCreated() 中初始化了 ViewModel。我用同样的方法附加了观察者。

在 FragmentA 中,我正在进行 API 调用,在 API 调用成功后,我将 FragmentA 替换为 FragmentB 和 addToBackStack。

现在真正的问题是当我按下 FragmentB 中的返回按钮时,返回堆栈中的 FragmentA 被调用,但又立即被 FragmentB 替换。

class FragmentA : Fragment(){
private var viewModel: TrackingViewModel?=null

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

private fun initViewModel(){
    viewModel = ViewModelProvider(requireActivity()).get(TrackingViewModel::class.java)
}

private fun attachObservers() {
    viewModel?.mResult?.observe(viewLifecycleOwner, {
        it?.let { resource ->
            parseResource(resource)
        }
    })
}

//Called this method on Button CLick in UI
private fun validate(data:String){
    viewModel?.coroutineSearch(data)
}

private fun parseResource(resource: Resource<GetsApiResponse>) {
    when (resource.status) {
        Status.SUCCESS -> {
            showLoading(false)
            //replaceFragmentWithBackStack is an Extension function
            replaceFragmentWithBackStack(FragmentB(), R.id.container)
        }
        Status.ERROR -> {
            showLoading(false)
            infoError(resource.responseCode)
        }
        Status.EXCEPTION -> {
            showLoading(false)
            infoException()
        }
        Status.LOADING -> {
            showLoading(true)
        }
    }
}

}

【问题讨论】:

  • 你使用导航组件吗?
  • @A.R.B.N 我没有使用导航组件。

标签: android fragment viewmodel


【解决方案1】:

对于使用LiveData 的每个人来说,在某些时候这是一个常见问题。这里的问题是由 intentional LiveData 行为引起的:一旦你开始观察它,它就会返回它存储的值(如果有的话)。 LiveData 主要设计用于视图/数据绑定解决方案。一旦 UI 组件观察到LiveData,它就应该接收该值并适当地显示它。所以你得到的行为是故意的。

包括我在内的许多其他开发人员都遇到过这个确切的问题。我能够通过使用在this post 中找到的作为解决此问题的推荐的事件包装器来解决它。它简单易懂,并且按照帖子中的说明工作。

使用此事件包装器,您的观察者代码将更新为:

private fun attachObservers() {
    viewModel?.mResult?.observe(viewLifecycleOwner, {
        it?.getContentIfNotHandled()?.let { resource ->
            // Only proceed if the event has never been handled
            parseResource(resource)
        }
    })
}

如果您想知道为什么首先您会立即收到来自此LiveData 的结果 - 这是因为您的视图模型已被缓存。当您使用 Activity 作为视图模型存储或存储所有者 (ViewModelProvider(requireActivity())) 时,您的视图模型将继续存在,直到您使用的 Activity 未被销毁。这意味着即使您通过按返回按钮离开FragmentA 到其上一个片段,然后通过创建一个新实例返回到FragmentA,您也将获得相同的视图模型。

事件包装源代码

/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

【讨论】:

    猜你喜欢
    • 2020-05-07
    • 1970-01-01
    • 2011-07-13
    • 1970-01-01
    • 2020-11-18
    • 1970-01-01
    • 1970-01-01
    • 2018-10-29
    • 1970-01-01
    相关资源
    最近更新 更多