当前位置: 首页 > 知识库问答 >
问题:

为什么当我导航回片段时会调用onChanged?

桂杰
2023-03-14

我有一个ViewModel处理我的业务逻辑,我正在使用Koin将它注入到我的活动和每个片段中。然而,在我从片段A-片段B导航并导航回片段A之后,我的观察者再次被触发。为什么会发生这种情况?当我返回时,如何阻止这种onChanged被触发?

我尝试将'this'和'view LifecycleOwner'设置为LiveData的LifecycleOwner。

我还尝试将observable移动到onCreate、onActivityCreated和onViewCreated

class MyViewModel : ViewModel() {

    private val _myData = MutableLiveData<Data>()
    val myData = LiveData<Data>()
        get() = _myData

    fun doSomething() {
        ... // some code
        _myData.postValue(myResult)
}
class Activity : BaseActivity() {

    private val viewModel by viewModel<MyViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        setSupportActionBar(main_toolbar)

        subscribeUI()
    }

    private fun subscribeUI() {
        myViewModel.isLoading.observe(this, Observer {
            toggleProgress(it)
        })
    }
}
class FragmentA : BaseFragment() {

    private val viewModel by sharedViewModel<MyViewModel>()

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        subscribeUI()
    }

    private fun subscribeUI() {
        viewModel.myData.observe(viewLifecycleOwner, Observer {
            val direction =
                FragmentADirections.actionAtoB()
            mainNavController?.navigate(direction)
        })
    }
}
class FragmentB : BaseFragment() {

    private val authViewModel by sharedViewModel<LoginViewModel>()

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        subscribeUI()
    }

    private fun subscribeUI() {
        viewModel.otherData.observe(viewLifecycleOwner, Observer {
            // Do something else...
        })
    }
}

共有1个答案

楚苏燕
2023-03-14

这是使用MutableLiveData时的预期行为。我认为你的问题与在哪里添加或删除订阅者无关。

MutableLiveData保存设置它的最后一个值。当我们回到前一个片段时,我们的LiveData观察再次收到现有值的通知。这是为了保留片段的状态,这正是LiveData的确切目的。

谷歌自己已经解决了这个问题,并提供了一种方法来克服这种行为,那就是使用事件包装器。

    null
    null
/**
* 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
}
class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
 override fun onChanged(event: Event<T>?) {
     event?.getContentIfNotHandled()?.let { value ->
         onEventUnhandledContent(value)
     }
 }
}
// Declare live data object
val testLiveData: MutableLiveData<Event<Boolean>
             by lazy{ MutableLiveData<Event<Boolean>>() }
testLiveData.postValue(Event(true))
viewModel?.testLiveData?.observe(this, EventObserver { result ->
 // Your actions
}
 类似资料:
  • 这里是链接的代码,我正在做同样的事情链接

  • 我正试着从后面取回碎片。它正在被检索,但问题是在按下后退按钮时,当前片段的oncreate视图和后续lifecyce方法也会被调用。下面是我将片段放入backbackback的代码: 以下是片段的代码:

  • 我正在使用registerForActivityResult,因为StartActivityForResult函数已被弃用。因此,我们正朝着使用registerForActivityResult的新方式迁移。这在活动中非常有效。然而,当在片段中使用这个函数时,永远不会调用回调函数。我还必须提到,父活动和其他子片段以旧的方式处理一些结果。调试代码时,我看到调用了父级的onActivityResult

  • 我有一个ViewPagerContainer片段,在应用程序启动时加载。ViewPagerContainer片段将两个选项卡(选项卡A和选项卡B)添加到操作栏。选项卡B有两个项目的列表视图。 我所做的:我在选项卡B片段中的列表视图项上附加了一个click listener,这样当用户单击一个项时,它会在第一个片段(即选项卡B下)内打开另一个片段(子片段)。 我陷入困境的地方:当用户按下后退按钮时,

  • 当我使用底部导航进行导航时,碎片会被破坏,计时器会重置到00:00。我用过计时器。我尝试了RetainInstance=true,但它只在屏幕旋转时才有帮助。 mainactivity.kt TimerFragment.kt

  • 我的应用程序有两个项目的导航抽屉:一个ViewPager(在一个片段内)和支持Map碎片。 ViewPager使用FragmentStatePagerAdapter并返回两个片段。 当我打开抽屉菜单并选择“项目1”(支持MapFrature)时,另一个碎片(ViewPager在其中)执行,但此方法不会破坏适配器创建的碎片,所以当我再次选择“项目0”时,我的应用程序创建的ViewPager与它的两个