これは私の実装です:
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)
1 つの考えとして、これは、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);
}
ただし、これはビューの破棄も遅らせます。
クラッシュがどのように発生するかについて何か考えはありますか? sharedViewModel
(これが問題の原因になる可能性がある場合に備えて、サンプルにKoin の注入を残しました。)