分段。 getViewLifeCycleOwner 不会阻止多次调用 LiveData Observer

Fragment. getViewLifeCycleOwner doesn't prevent multiple calls of LiveData Observer

我使用 Clean Architecture、LiveData、导航组件和底部导航视图。
我正在创建一个带有 三个选项卡 的简单应用程序。默认情况下,第一个选项卡 Fragment 使用一些 API 加载用户数据。当我转到另一个选项卡然后 return 到第一个选项卡片段时,我看到 observe return 一个新数据!

当我切换回第一个选项卡时,我需要 observe 不要再次 return 数据!我究竟做错了什么?你能帮帮我吗?

P.s。对于导航,我使用来自 navigation-advanced-sample 的示例,并且在切换选项卡后 onDestroy 未被调用。

第一个解决方案在文章Observe LiveData from ViewModel in Fragment中说:

One proper solution is to use getViewLifeCycleOwner() as LifeCycleOwer while observing LiveData inside onActivityCreated as follows.

我使用以下代码,但它对我不起作用:

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    Timber.d("onActivityCreated")
    viewModel.getProfileLive().observe(viewLifecycleOwner, observer)
}

文章Architecture Components pitfalls — Part 1中的第二种解决方案建议使用重置现有观察者手动取消订阅onDestroyView() 中的观察者。但它对我也不起作用...

ProfileFragment.kt

class ProfileFragment : DaggerFragment() {

    @Inject
    lateinit var viewModel: ProfileFragmentViewModel

    private val observer = Observer<Resource<Profile>> {
        when (it.status) {
            Resource.Status.LOADING -> {
                Timber.i("Loading...")
            }
            Resource.Status.SUCCESS -> {
                Timber.i("Success: %s", it.data)
            }
            Resource.Status.ERROR -> {
                Timber.i("Error: %s", it.message)
            }
        }
    };

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Timber.d("onCreate")
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        Timber.d("onCreateView")
        return inflater.inflate(R.layout.fragment_profile, container, false)
    }

    fun <T> LiveData<T>.reObserve(owner: LifecycleOwner, observer: Observer<T>) {
        removeObserver(observer)
        observe(owner, observer)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Timber.d("onViewCreated")
        viewModel.getProfileLive().observe(viewLifecycleOwner, observer)
        // viewModel.getProfileLive().reObserve(viewLifecycleOwner, observer)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        Timber.d("onActivityCreated")
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Timber.d("onDestroyView")
        // viewModel.getProfileLive().removeObserver(observer)
    }

    override fun onDestroy() {
        super.onDestroy()
        Timber.d("onDestroy")
    }

    override fun onDetach() {
        super.onDetach()
        Timber.d("onDetach")
    }
}

ProfileFragmentViewModel.kt

class ProfileFragmentViewModel @Inject constructor(
    private val profileUseCase: ProfileUseCase
) : ViewModel() {

    init {
        Timber.d("Init profile VM")
    }

    fun getProfileLive() = profileUseCase.getProfile()
}

ProfileUseCase

class ProfileUseCase @Inject constructor(
    private val profileRepository: ProfileRepository
) {

    fun getProfile(): LiveData<Resource<Profile>> {
        return profileRepository.getProfile()
    }
}

ProfileRepository.kt。 class ProfileRepository @Inject 构造函数( 私有 val loginUserDao:LoginUserDao, 私有 val profileDao:ProfileDao, ) {

fun getProfile(): LiveData<Resource<Profile>> =
    liveData(Dispatchers.IO)
    {
        emit(Resource.loading(data = null))

        val profile = profileDao.getProfile()

        // Emit Success result...
    }

}

这是因为 片段生命周期 的工作方式。当您在片段之间来回移动时,onViewCreated() 会再次调用。在 onViewCreated 中,您正在调用 viewModel.getProfileLive() returns 来自 repositoryobserve 的实时数据它。

由于每次您返回 Fragment 时都会调用 onViewCreated(),因此您会调用 viewModel.getProfileLive(),进而调用 repository 被再次调用,再次触发 Fragment 中的 observe 方法。

为了解决这个问题, 在 ViewModel 中创建一个 LiveData 变量,将其设置为从 返回的 Live Data ]知识库。 在 Fragment 中观察 ViewModelLiveData 变量 而不是从 知识库。 这样,您的 observe 方法将在第一次触发,并且仅当来自 repository 的数据的 value 发生变化时才会触发。