마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

Fragment안에 viewpager2를 사용할 때 FragmentStateAdapter의 하위 item들은 어떻게 종료하나요?

0 추천
제목대로 A라는 fragment 안에 viewpager2와 FragmentStateAdapter를 사용하여 페이징이 가능하도록 만들고 있습니다.

(adapter fragment item은 test1, test2, test3 이라 지칭)

그런데 A fragment가 destory될 때 test1, test2, test3 fragment가 화면에는 안보이지만 destory되지 않고 살아있는거(유지) 같더라구요.

이러면 계속 메모리를 잡아먹을거 같은데 A fragment가 destory될때 하위 아이템들은 어떻게 destroy 시키나요??

생각해본 방법은 A fragment가 destroy될 때 Test1 test1 = new Test(); 로만들어진 fragment의 ondestroy를 직접 호출하는 형식이거나 getFragmentManager().beginTransaction().remove(test1).commit();를 호출해서 제거하는 방법인데 이게 맞을까요? 아니면 다른 무언가가 있을까요???
로아 (390 포인트) 님이 2021년 1월 7일 질문

2개의 답변

0 추천
 
채택된 답변
그게 뷰페이져가 동작하는 방식입니다. 페이지를 이동할 때마다 뷰를 그려주어야 하기 때문에 앞뒤로 있는 뷰를 보관해 두었다가 재사용하는 겁니다. 리사이클러뷰와 비슷합니다. 안그러면, 페이지가 많을 때 퍼포먼스의 문제가 생길 수 있어요.
spark (227,830 포인트) 님이 2021년 1월 8일 답변
로아님이 2021년 1월 20일 채택됨
그부분은 인지하고 있습니다
그런데 T.fragment - 이동 -> A.fragment생성 (Viewpager포함) -> A.fragment종료
위와 같은 이동을 했을 때 A.framgnet의 viewpager 하위 뷰가 종료되지 않으면계속해서 유지되는거 아닌가요??
다시 new A()로 A.fragment 생성 후 이동한다고 해도 저장되었던 A.fragment가 불러오는게 아니라 A-1.fragment가 생성되고 불러와지는거 아닌가요??
그렇다면 A.fragment가 종료될때 하위뷰도 함께 종료되지 않는다면 메모리만 차지하게 되는거 아닌지요
0 추천

프레그먼트를 10개 추가하고 ViewPager2의 로그를 확인해 봤습니다. 페이지 0 부터 9까지 이동했다가, 거꾸로 페이지 9에서 0으로 이동할 때의 로그입니다.

ScreenSlidePageFragment.onCreate: CurrentPage: 0: null
ScreenSlidePageFragment.onCreate: CurrentPage: 1: null
ScreenSlidePageFragment.onCreate: CurrentPage: 2: null
ScreenSlidePageFragment.onCreate: CurrentPage: 3: null

ScreenSlidePageFragment.onDestroy: CurrentPage: 9
ScreenSlidePageFragment.onCreate: CurrentPage: 5: Bundle[{androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:view_state={2131231074=android.view.AbsSavedState$1@f216706}}]

ScreenSlidePageFragment.onDestroy: CurrentPage: 8
ScreenSlidePageFragment.onCreate: CurrentPage: 4: Bundle[{androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:view_state={2131231074=android.view.AbsSavedState$1@f216706}}]

ScreenSlidePageFragment.onDestroy: CurrentPage: 7
ScreenSlidePageFragment.onCreate: CurrentPage: 3: Bundle[{androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:view_state={2131231074=android.view.AbsSavedState$1@f216706}}]

ScreenSlidePageFragment.onDestroy: CurrentPage: 6
ScreenSlidePageFragment.onCreate: CurrentPage: 2: Bundle[{androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:view_state={2131231074=android.view.AbsSavedState$1@f216706}}]

ScreenSlidePageFragment.onDestroy: CurrentPage: 5
ScreenSlidePageFragment.onCreate: CurrentPage: 1: Bundle[{androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:view_state={2131231074=android.view.AbsSavedState$1@f216706}}]

ScreenSlidePageFragment.onDestroy: CurrentPage: 4
ScreenSlidePageFragment.onCreate: CurrentPage: 0: Bundle[{androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:view_state={2131231074=android.view.AbsSavedState$1@f216706

처음에 네번째 페이지까지는 destory하는 프레그먼트는 없고 페이지 이동시마다 새로 생성합니다.
다섯번째 페이지로 이동하면, 첫번째 페이지에 프레그먼트를 종료합니다. 여기서부터 마지막페이지까지는 동일하게 
currentPage-3 에 있는 프레그먼트를 destory합니다. 
그런데 다시 페이지 9에서 거꾸로 이동할 때의 로그를 보시면 Bundle이 추가된 것을 보실 수 있습니다. 이건 onCreate에 있는 savedInstanceState의 값입니다. 즉 프레그먼트는 destory되지만 뷰상태의 복구를 위해서 뷰페이저가 뷰정보만 보관했다가 프레그먼트를 새로 생성하면서 savedInstanceState로 전달해서 복구할 수있도록 해주고 있습니다. 

아마 님의 경우도 찍어보면 똑같은 걸 발견하실 수 있을겁니다. 이 부분은 메모리를 차지한다고 보기 보다는 뷰의 상태를 복구한다는 부분에 초점을 맞추는 것이 더 적합하다고 봅니다.

그리고  ViewPager2는 내부적으로는 RecyclerView의 같은 방식으로 뷰를 재사용하도록 설계되었습니다. 아직 더 들여다 보지는않았는데, RecycledPool와 Cache가 존재할 거라고 생각합니다. 이것들을 통해서 얼마나 많은 페이지를 캐슁하거나 풀어 넣을지 결정할 겁니다. RecyclerView처럼 캐쉬를 조작할 수 있도록 허용하는지는 아직 찾아보지 않아서 확실하지 않네요.

spark (227,830 포인트) 님이 2021년 1월 9일 답변
친절한 답변 감사합니다.
다만 답변이 제 질문과는 조금 어긋난것 같습니다.. 제가 잘못 이해한 것일 수도 있구요..

제 질문은 A.fragment에 위치한 viewpager2의 "페이지간 이동했을 때" 페이지프레그먼트의 destroy에 대한 질문이 아니라 "A.fragment를 destroy 시켰을 때 생성되었던  페이지프레그먼트들이 언제 destroy되는지, 자동으로 종료되는게 아니라면 어떻게 직접 종료시키는지"에 대한 질문이었습니다.

fragment 스택을 보면

Q.fragment -> A.fragment (viewpager2가 있고 adapter를 통해 페이지프레그먼트생성) -> A.fragmet destroy 후 Q.fragment로 이동 이런 상황이었을때 제 로그에는 생성되었던 페이지프레그먼트들(A-1, A-2 등등)이 A.fragment destroy때 destroy로그가 찍히지 않더라구요.

결론은 A.fragment는 종료가 되었지만 페이지프레그먼트들은 자동으로 destroy되지 않았다 인데 맞는건가요?? 아니면 제가 잘못 만든걸까요
가장 간단한 방법은 adapter에서 제거하시면 되겠죠. 아니면adapter를 null로 만드세요.
왜 이런 동작을 원하시는지 궁금합니다. 단순히 메모리 문제라면 뷰페이져 쓰지 마시고 replace를 써서 프레그먼트를 직접관리하세요.
...