Deco Calendar Android版開発で困ったこと-FragmentStateAdapterとTransactionTooLargeException
Deco Calendar AppのAndroid版開発で厄介なことが起こりました。解決に数日かかりました...
このアプリは、スワイプ操作で月めくりがなされ、画面を何枚も用意する必要があります。その機能を実現するために、Android版ではViewPager2を用いています。そしてFragmentを何枚も用意する必要がありますので、ViewPager2のアダプターとしてFragmentStateAdapterを用いています。
困ったことに、このアプリでそれなりの容量の壁紙を設定すると、Activityを切り替えたときに落ちる現象が見られるようになりました。デバッグログを見ると、” TransactionTooLargeException”と表示されています。Androidでは、Activityが切り替わるときに、そのActivity(Fragment)のビットマップデータをBundlerに詰め込む作業が行われるのですが、1MBを超えると、上記例外が発生するようです。Fragmentにそれなりの容量のビットマップを貼り付けているのですから、起こって然るべきかな、と思います。
その回避方法は結構悩みました。Fragmentは単一のものではなく、Pagerですし。
回避方法は、暫定的ではあるのですが、一旦FragmentStateAdapterで生成されたFragmentをremoveして、再びActivityが復帰したときに再描画を行うことにしました。
親FragmentのonPause
override fun onPause() {
super.onPause()
//adapterに紐付けるitemについて一旦空のものを設定する
val dmmyItem = arrayListOf<Item>()
val adapter = viewPager2.adapter as BaseFragmentPageAdapter
adapter.updateList(dmmyItem) // DiffUtil.calculateDiff用のfunction
adapter.removeFragments()
}
FragmentStateAdapter内
private var frags = arrayListOf<Fragment>()
private var item = mutableListOf<Item>() //ビットマップ等の情報を格納したdataclass
//中略
override fun createFragment(position: Int): Fragment {
val fragment = CalendarFragment.newInstance(getItem(position), holidays)
frags.add(calendarFragment)
return calendarFragment
}
fun removeFragments() {
val iter = frags.iterator() //createFragmentでFragmentをArrayListに格納する
while(iter.hasNext()) {
val fragment = iter.next()
fm.beginTransaction().remove(fragment).commit()
iter.remove()
}
}
そして、親FragmentのonResumeが呼ばれたときに、Fragmentを再生成する作業をおこなっています。つまり、メモリに格納したデータを呼び出してそこで再描画(実際にはDiffutil.calculateをかけるfunctionであるupdateListを呼び出しているだけ)をおこなっています。
これでなんとかActivityが切り替わるときに例外が発生しなくなりました。パラパラマンガアプリやスライドショーなどのアプリを作る際にViewPager2は便利で必須ですが、落とし穴には注意しましょう。
この記事が気に入ったらサポートをしてみませんか?