LoginSignup
13
5

More than 1 year has passed since last update.

Jetpack ComposeのViewCompositionStrategyの挙動まとめ

Last updated at Posted at 2022-11-10

たくさんあってよく分からなくなるので、それぞれのコールバックからの挙動のまとめを作っておきます。

ComposeViewに対してsetContent{}を呼ぶときに、以下のように変更してあげることで、Composeのdisposeするタイミングを変更してあげることができます。
これによってメモリリークを減らせるそうです。

composeView.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)

まとめ

そんなに難しくないコードになっていて、コードを読むと以下のようになっているのが分かると思います。
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ViewCompositionStrategy.android.kt

クラス名 onViewAttachedToWindow時の挙動 onViewDetachedFromWindow時の挙動 PoolingContainerListener.onRelease()時 lifecycleがdestroy
DisposeOnDetachedFromWindowOr
ReleasedFromPool(デフォルト)
何もしない ViewがpollingContainer内でない場合はview.disposeComposition()
(ViewがpollingContainer内の場合はPoolingContainerListener.onRelease()がWindowから消えるときも呼ばれる)
view.disposeComposition() 何もしない
DisposeOnDetachedFromWindow 何もしない view.disposeComposition() 何もしない 何もしない
DisposeOnLifecycleDestroyed(lifecycle)
引数にlifecycleを渡すことで使える
何もしない 何もしない 何もしない view.disposeComposition()
DisposeOnViewTreeLifecycleDestroyed
Windowにすでにアタッチされている場合
ViewTreeLifecycleOwner.get(view)からlifecycleを取得する
何もしない 何もしない 何もしない view.disposeComposition()
DisposeOnViewTreeLifecycleDestroyed
Windowにアタッチされていない場合
ここでViewTreeLifecycleOwner.get(view)からlifecycleを取得 何もしない 何もしない view.disposeComposition()

それぞれいつ呼ばれるの?

onViewAttachedToWindowとonViewDetachedFromWindow

onViewAttachedToWindow()はwindowにアタッチされたとき
onViewDetachedFromWindowはwindowからデタッチされたとき。

    public interface OnAttachStateChangeListener {
        /**
         * Called when the view is attached to a window.
         * @param v The view that was attached
         */
        public void onViewAttachedToWindow(View v);
        /**
         * Called when the view is detached from a window.
         * @param v The view that was detached
         */
        public void onViewDetachedFromWindow(View v);
    }

PoolingContainerListener.onRelease()

これはRecyclerViewの中にComposeViewがいるときに呼ばれます。
なぜこのイベントを使う必要があるのかというとRecyclerViewはリサイクルするために、子ViewをViewから外したりつけたりして管理するので、単純にWindowから外れたかどうかでライフサイクルでは管理できず、また親のViewのライフサイクルを持ってくるやり方ではRecyclerViewとしてはもう消しているViewでも保持し続けてしまうためです。

PoolingContainerListener.onRelease()はコンテナのView(RecyclerView)がViewを破棄したか、コンテナ自身がウインドウから消えたときに呼ばれる。

onRelease()
Signals that this view should dispose any resources it may be holding onto, because its container is either discarding the View or has been removed from the hierarchy itself.

ドキュメント通り、RecyclerView.onDetachedFromWindow()からも呼ばれていました

RecylerViewのコード

image.png

スクロールしたりして、pool(パフォーマンスのために対比させておく場所)にViewHolderを入れるときにもうすでにpoolがいっぱいだったときにすぐに呼んだりなど

RecylerViewのコード
image.png

Destroy

これはおなじみのライフサイクルのものです。
基本的にはインスタンスで一回しか呼ばれないものです。

ViewTreeLifecycleOwner.get()について

ライフサイクルオーナーはライフサイクルを持っていて、それを利用できます。基本的にはActivityかFragmentになります。
これはどこのライフサイクルオーナー取れるのかということで下記のようにViewのparentから取得するようです。

ViewTreeLifecycleOwnerのコード

    @Nullable
    public static LifecycleOwner get(@NonNull View view) {
        LifecycleOwner found = (LifecycleOwner) view.getTag(R.id.view_tree_lifecycle_owner);
        if (found != null) return found;
        ViewParent parent = view.getParent();
        while (found == null && parent instanceof View) {
            final View parentView = (View) parent;
            found = (LifecycleOwner) parentView.getTag(R.id.view_tree_lifecycle_owner);
            parent = parentView.getParent();
        }
        return found;
    }

また以下がViewTreeOwnerたちです。
image.png

どれを使うべきか?

デフォルトのDisposeOnDetachedFromWindowOrReleasedFromPoolではFragmentやTransitionや独自のライフサイクルの管理に向かないという言及があります

By default, Compose uses the DisposeOnDetachedFromWindowOrReleasedFromPool strategy. However, this default value might be undesirable in some situations when the Compose UI View types are used in:
Fragments. The Composition must follow the fragment's view lifecycle for Compose UI View types to save state.
Transitions. Anytime the Compose UI View is used as part of a transition, it will be detached from its window when the transition starts instead of when the transition ends, thus causing your composable to dispose of its state while it is still on screen.
Your own lifecycle-managed custom View.

DisposeOnDetachedFromWindowOrReleasedFromPoolはRecyclerViewとの連携があるので、その場合やActivityなどでは良いのかもしれません。

FragmentではDisposeOnViewTreeLifecycleDestroyedを使う例があり、下記の記述もあるので、こちらのほうが良いです。

Fragments. The Composition must follow the fragment's view lifecycle for Compose UI View types to save state.

独自のライフサイクルではDisposeOnLifecycleDestroyedが使えます。

13
5
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
13
5