パフォーマンスチューニングと書くと大げさかもしれない。 最近何度か RecyclerViewのパフォーマンス面の修正をする機会があったので、最低限これくらい見ておくと良さそう、という項目についてまとめました。
各アイテムのstable ID
Adapterの各アイテムが固有のIDを持つ場合、stable IDを利用するとnotifyDataSetChanged()
呼び出し時の各アイテム再描画など様々な場面で有利になります。
利用するためには、AdapterのgetItemId(int position)
を実装した上でsetHasStableIds(true)
を呼び出します。
notify〜系を正しく呼び出す
Adapterの内容を更新する際、Adapter#notifyDataSetChanged()
を呼んでしまいがちですがnotifyItemInserted()
やnotifyItemRemoved()
を適切に呼び出すことで不要なViewの再描画を防ぐことができます。
この修正もstable IDを実装しておくとより効果が高くなりそうです。
なお、Support Library 24.2.0 からは DiffUtilが追加され、notify〜を個別に呼ばなくてもリストの比較処理を行うメソッドをいくつか実装するだけでadapterの更新ができるようになりました。
https://developer.android.com/reference/android/support/v7/util/DiffUtil.html
アイテムのキャッシュ
LinearLayoutManagerの場合、getExtraLayoutSpace()
をOverrideし、大きな値を返すことによってアイテムをプリキャッシュさせることができます。
これにより、プリキャッシュのためのコストと引き換えにスクロール時のパフォーマンスを改善させることができます。
どのような値が適しているかは状況次第ですが、LinearLayoutManagerのorientationにあわせて画面の縦横サイズをそのまま返す実装が一般的なようです(多分)。
また、RecyclerView
自体もsetItemViewCacheSize()
というメソッドによりViewキャッシュの個数を増やすことができ、こちらもスクロール時のパフォーマンスに影響します。
onViewRecycled を活用する
onBindViewHolder()
で時間のかかる処理を行っている場合、onViewRecycled()
でキャンセルする処理を入れましょう。
RecyclerViewサイズの固定化
Adapterの内容がRecyclerViewのサイズに影響しない場合、setHasFixedSize (true)
を設定することでパフォーマンスが向上します。
ネストされたRecyclerViewのプリフェッチ
縦方向のRecyclerViewの中に横方向のRecyclerViewをネストする、いわゆるカルーセル表示のUIの場合、setInitialPrefetchItemCount
で初期表示されるアイテムの個数を設定しておくことで親RecyclerViewのスクロール時に有利になるようです。
(Support Library 25.1.0で追加されましたが、まだ十分検証していません)
Adapter差し替え処理の最適化
何らかの理由でRecyclerViewに同一クラス/別インスタスなAdapterを設定する場合、setAdapter()
の代わりにswapAdapter()
を利用することができます。
swapAdapter()
はRecycledViewPoolをクリアしないためViewが再利用されパフォーマンス面で有利となります。 先述の stable ID を設定している場合はより有用です。
LayoutParamsを動的に生成しない
RecyclerViewnに限った話ではないですが、LayoutParams
を動的に生成してsetするのは避けたほうが良いです。
稀にStaggeredGridLayoutManager.LayoutParams
のsetFullSpan()
を呼び出すためだけに動的に生成しているコードを見かけますが、通常は アイテムのroot viewからgetLayoutParams()
するとStaggeredGridLayoutManager.LayoutParams
になっているので、これを再利用してください。
これはパフォーマンスへの影響ももちろんですが、レイアウトxmlに記述したlayout_widthやlayout_height、marginの設定消失を避ける意味でも重要です。