8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RecycledViewPoolを共有する場合はPoolが保持可能なViewの個数に気をつけよう

Posted at

TL;DR

  • RecycledViewPool は、デフォルトでは、View Typeごとに最大5つまでしかViewを保持しない設定になっています
  • 複数の RecyclerView で単一の RecycledViewPool を共有している場合は、RecycledViewPool が保持するViewの最大数を RecycledViewPool.setMaxRecycledViews() で増やしておくと、RecycledViewPool共有の効果がさらに高まるかもしれません
setMaxRecycledViews()でプールサイズを増やす例
val viewPool = RecyclerView.RecycledViewPool()
viewPool.setMaxRecycledViews(VIEW_TYPE_HEADER, 128)
viewPool.setMaxRecycledViews(VIEW_TYPE_ITEM, 128)

きっかけ

RecyclerViewのViewPoolを共有してInflate回数を劇的に減らす」を読んでRecycledViewPoolを複数のRecyclerViewで共有できることを知り、RecyclerViewViewPager の中で使っている画面で試してみました。

このテクニックを適用した場合、RecyclerViewをスクロールしたり ViewPagerのページを移動したりして、ある程度の数のViewをInflationすれば、あとは新しいViewの表示は全て RecycledViewPool からのリサイクルでまかなえるようになり、ViewのInflationはなくなると思っていました。ただ、実際にやってみると、いつまでたっても、ページを移動するたびに何個かViewがInflationされる挙動が止まりませんでした。

変だな、と思って調べたところ、RecycledViewPool が保持するViewの最大数がデフォルトではView Typeごとに5個に設定されており、Poolサイズが足りていないことが原因だとわかった、という次第です。

RecycledViewPoolが保持するViewの数

RecycledViewPoolは、View Typeごとに保持するViewの数に制限を設けており、その数を超えてViewをリサイクルしようとすると、当該Viewは保持されずに破棄されます

If the pool is already full for that ViewHolder's type, it will be immediately discarded.

この最大保持数は、デフォルトでは 5 に設定されています (参考: RecyclerView.java#5519)

RecyclerView.java
    public static class RecycledViewPool {
        private static final int DEFAULT_MAX_SCRAP = 5;

        static class ScrapData {
            final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
            int mMaxScrap = DEFAULT_MAX_SCRAP;
            long mCreateRunningAverageNs = 0;
            long mBindRunningAverageNs = 0;
        }

RecycledViewPool を共有しない場合は、Viewがリサイクルされるのはスクロールにより画面外に出た場合なので、View Typeごとに5個保持されれば十分そうです。一方、RecycledViewPool を共有する場合は、RecyclerView が使われなくなったらその RecyclerView が表示していたViewを全部リサイクルしたいので、5個では足りないケースが多そうです。

View Typeごとの保持最大数は RecycledViewPool.setMaxRecycledViews() で変更できるようになっています。大きい値を設定したら即座にView Poolが伸長される、ということもないので、大きめの値を設定しても特に問題ないです (参考: RecyclerView.java#5561)。

弊社アプリでは、試しに 128 を設定したところ、ある程度の数のViewがInflationされた後は、ViewPager のページを移動したり RecyclerView をスクロールしたりしてもViewがInflationされなくなり、期待通りの動作となりました。

おまけ: そもそもRecyclerViewが破棄されたらViewはリサイクルされるのか

LinearLayoutManager やその派生クラスの GridLayoutManager を使っている場合は、LinearLayoutManager.setRecycleChildrenOnDetach()true を設定しておけば、RecyclerView がWindowからDetachされるときにViewがリサイクルされます1

仕組みとしては、RecyclerView がWindowからDetachされたときに、LaoutManageronDetachedFromWindow() が呼ばれるのですが、LinearLayoutManager は、setRecycleChildrenOnDetach()true が設定されていたら、onDetachedFromWindow() において removeAndRecycleAllViews() を呼び出して全てのViewをリサイクルする、ということのようです (参考: LinearLayoutManager.java#231)。

LinearLayoutManager.java
    @Override
    public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
        super.onDetachedFromWindow(view, recycler);
        if (mRecycleChildrenOnDetach) {
            removeAndRecycleAllViews(recycler);
            recycler.clear();
        }
    }
  1. RecyclerViewのViewPoolを共有してInflate回数を劇的に減らす」でも紹介されています

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?