FragmentManager is already executing transactionsと言われた時、Fragmentの初期処理で例外が起きていないか確認する
(この現象はAndroid6.0, support-lib ver.24.2.1で確認した。)
次のような例外が出た。メッセージを読むと「トランザクションをすでに実行しているところだ」と言われているようなので、commit()
するタイミングが悪かったのかななどを考えていろいろ試してみるも効果なし。ググるとcommitNow()
するといいよみたいなStackOverflowのやり取りが出てきたりするが、効果なし。
java.lang.IllegalStateException: FragmentManager is already executing transactions at android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:1626) at android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:679) at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:143) at android.support.v4.view.ViewPager.populate(ViewPager.java:1240) at android.support.v4.view.ViewPager.populate(ViewPager.java:1088) at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1614) at android.view.View.measure(View.java:18788) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:700) at android.support.design.widget.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:90) at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1364) at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:765) at android.view.View.measure(View.java:18788) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:135) at android.view.View.measure(View.java:18788) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465) at android.widget.LinearLayout.measureVertical(LinearLayout.java:748) at android.widget.LinearLayout.onMeasure(LinearLayout.java:630) at android.view.View.measure(View.java:18788) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) at android.view.View.measure(View.java:18788) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465) at android.widget.LinearLayout.measureVertical(LinearLayout.java:748) at android.widget.LinearLayout.onMeasure(LinearLayout.java:630) at android.view.View.measure(View.java:18788) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2643) at android.view.View.measure(View.java:18788) at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2100) at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1216) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1452) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858) at android.view.Choreographer.doCallbacks(Choreographer.java:670) at android.view.Choreographer.doFrame(Choreographer.java:606) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
スタックトレースをみるとViewPagerの各ページを作る途中に起きているということぐらいはわかるが、私の書いたどのコードが発端になっているのかまではわからなかった。スタックトレースの一番上のメソッドを見てみると、
public void execSingleAction(Runnable action, boolean allowStateLoss) { if (mExecutingActions) { throw new IllegalStateException("FragmentManager is already executing transactions"); } if (Looper.myLooper() != mHost.getHandler().getLooper()) { throw new IllegalStateException("Must be called from main thread of fragment host"); } if (!allowStateLoss) { checkStateLoss(); } mExecutingActions = true; action.run(); mExecutingActions = false; doPendingDeferredStart(); }
となっている。mExecutingActions==true
であるために起きているということなのだが、これを書き換えているのはそのすぐ下と、FragmentManagerImpl#execPengingActions()
の中。どっちもRunnable#run()
の前後にフラグのon/offをやっている。Runnable#run()
のなかで例外が起きてしまうとmExecutingAction
がtrueのままになるのか、と考えて追加したコードのバグを取ったら期待したとおりに動いた。
FragmentManagerImpl#execSingleAction()
は比較的最近追加されたAPIであるようす。action.run()
の例外がどこで握りつぶされているのかなどの解析はまだしていない。このエラーメッセージでぐぐる日本人の時間を少しでも救えればいいかなと思って書いた。