首页 > 解决方案 > 使用视图绑定和实时数据观察者在 Fragment 中崩溃

问题描述

这是我的实现:

class SampleFragment : Fragment()
{
    private val viewModel by sharedViewModel<MyViewModel>()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
            FragmentSampleBinding.inflate(inflater, container, false).apply {
                myButton.setOnClickListener { viewModel.foo() }

                viewModel.statusLiveData.observe(viewLifecycleOwner, Observer {
                    titleTextView.text = it.status.title
                    myButton.text = it.status.button_title
                })
            }.root
}

在极少数情况下,会发生这种崩溃:

java.lang.NullPointerException: 
  at com.example.fragments.SampleFragment$onCreateView$1$2.onChanged (SampleFragment.kt:2)
  at com.example.fragments.SampleFragment$onCreateView$1$2.onChanged (SampleFragment.kt:1)
  at androidx.lifecycle.LiveData.considerNotify (LiveData.java:6)
  at androidx.lifecycle.LiveData.dispatchingValue (LiveData.java:5)
  at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged (LiveData.java:10)
  at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged (LiveData.java:3)
  at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent (LifecycleRegistry.java:3)
  at androidx.lifecycle.LifecycleRegistry.forwardPass (LifecycleRegistry.java:9)
  at androidx.lifecycle.LifecycleRegistry.sync (LifecycleRegistry.java:9)
  at androidx.lifecycle.LifecycleRegistry.moveToState (LifecycleRegistry.java:5)
  at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent (LifecycleRegistry.java:2)
  at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent (FragmentViewLifecycleOwner.java:1)
  at androidx.fragment.app.Fragment.performStart (Fragment.java:9)
  at androidx.fragment.app.FragmentStateManager.start (FragmentStateManager.java:3)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:28)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:69)
  at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState (FragmentManager.java:4)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:75)
  at androidx.fragment.app.BackStackRecord.executeOps (BackStackRecord.java:28)
  at androidx.fragment.app.FragmentManager.executeOps (FragmentManager.java:6)
  at androidx.fragment.app.FragmentManager.executeOpsTogether (FragmentManager.java:14)
  at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute (FragmentManager.java:10)
  at androidx.fragment.app.FragmentManager.execPendingActions (FragmentManager.java:4)
  at androidx.fragment.app.FragmentManager$4.run (FragmentManager.java:1)
  at android.os.Handler.handleCallback (Handler.java:751)
  at android.os.Handler.dispatchMessage (Handler.java:95)
  at android.os.Looper.loop (Looper.java:154)
  at android.app.ActivityThread.main (ActivityThread.java:6823)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1563)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1451)

一种想法是,当视图在observe()订阅结束之前被销毁时会发生这种情况。

我挖掘了片段代码并在 FragmentManager 中找到了这个:

private void destroyFragmentView(@NonNull Fragment fragment) {
    fragment.performDestroyView();
    mLifecycleCallbacksDispatcher.dispatchOnFragmentViewDestroyed(fragment, false);
    fragment.mContainer = null;
    fragment.mView = null;
    // Set here to ensure that Observers are called after
    // the Fragment's view is set to null
    fragment.mViewLifecycleOwner = null;
    fragment.mViewLifecycleOwnerLiveData.setValue(null);
    fragment.mInLayout = false;
}

viewLifecycleOwner在视图被破坏后,它实际上被破坏了。但是由于这个函数是不可暂停的,所以应该没有办法从主线程交错。

我的假设正确吗?

我进一步发现,viewLifecycleOwner如果存在退出转换(FragmentManager#moveToState() Lines 1261+),则可以长寿:

// If a fragment has an exit animation (or transition), do not destroy
// its view immediately and set the state after animating
if (mExitAnimationCancellationSignals.get(f) == null) {
    destroyFragmentView(f);
} else {
    f.setStateAfterAnimating(newState);
}

然而,这也会延迟视图的破坏。

你知道崩溃是如何发生的吗?(我在样品中留下了 Koin 注射液,sharedViewModel以防这可能是问题的根源。)

标签: androidandroid-fragmentsandroid-lifecycleandroid-livedataandroid-viewbinding

解决方案


推荐阅读