Library 기준
androidx.fragment:fragment-ktx:1.5.7
androidx.recyclerview:recyclerview:1.3.0
Fragment Lifecycle#
Fragment.java
파일과 공식 문서 를 보면 알 수 있듯이, Fragment는 mLifecycleRegistry
와 mViewLifecycleOwner
두 개의 Lifecycle를 가지고 있다.
Fragment 생명주기
를 View lifecycle 과 Fragment 자체 lifecycle로 분리해서 바라보면, callback 함수명에서 알 수 있듯이,
onDestoryView()
호출로 Fragment의 View는 사라지고, Fragment는 onDetach()
호출 전과 onDestoryView()
호출 후에 onDestory()
가 호출되어 사라진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| //Fragment.java
private void initLifecycle() {
mLifecycleRegistry = new LifecycleRegistry (this);
// ...
}
// ...
@MainThread
@NonNull
public LifecycleOwner getViewLifecycleOwner() {
// ...
return mViewLifecycleOwner;
}
// ...
@Override
@NonNull
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| void performStart () { // 예시 performStart() 함수, mLifecycleRegistry, mViewLifecycleOwner을 모두 이용하는 모습
mChildFragmentManager.noteStateNotSaved();
mChildFragmentManager.execPendingActions(true);
mState = STARTED;
mCalled = false;
onStart();
if (!mCalled) {
throw new SuperNotCalledException ("Fragment " + this
+ " did not call through to super.onStart()");
}
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
if (mView != null) {
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
mChildFragmentManager.dispatchStart();
}
|
RecyclerView 내부 구조#
RecyclerView.java
내부를 보면, Adapter
는 mObservable
을 가지고 있고, Observer들은 RecyclerView
를 참조한다.
또한 RecyclerView
는 Adapter
를 참조하기에,
Adatper
와 RecyclerView
는 양방향 참조, cycle
이 생긴다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| // RecyclerView.java
public class RecyclerView extends ViewGroup implements ScrollingView,
NestedScrollingChild2, NestedScrollingChild3 {
//...
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
//...
Adapter mAdapter;
//...
}
private class RecyclerViewDataObserver extends AdapterDataObserver {
// ...
}
public abstract static class Adapter<VH extends ViewHolder> {
//...
private final AdapterDataObservable mObservable = new AdapterDataObservable();
//...
}
static class AdapterDataObservable extends Observable<AdapterDataObserver> {
public boolean hasObservers() { return !mObservers.isEmpty(); }
//...
}
|
Memory Leak 발생 가능성#
BottomNaviagtion
을 사용하는 상황BottomNavView
에 A,B,C 3개의 Fragment가 menu로 등록된 상태- 현재 A_Fragment가
Resume
인 상황에서, B_Fragment로 전환시에- A_Fragment는
onDestoryView()
가 호출 RecyclerView
가 사라질때, mOberver
참조로 인해 Memory Leak
발생
View or Data Binding
를 사용할때도 유사하게 Memory Leak
이 발생 할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
| class A_Fragment : Fragment() {
private lateinit var mockAdapter: MockAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mockAdapter = MockAdapter()
val recyclerView: RecyclerView = view.findViewById(R.id.recycler_view)
recyclerView.layoutManager = LinearLayoutManager(this.context)
recyclerView.adapter = mockAdapter
}
}
|
LeakCanary
을 통해 확인한 Memory Leak
상태
해결 방법#
Adatper
와 RecyclerView
의 양방향 참조
를 onDestoryView()
시에 해제
- mockAdapter 를 nullable 하게 설정
onDestoryView()
호출시 mockAdapter = null
- B_Fragment에서 다시 A_Fragment 로 전환시, Fragment는
onCreateView()
부터 생명주기 다시 시작 onCreateView()
호출시에 mockAdapter를 MockAdapeter() 로 초기화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| class A_Fragment : Fragment() {
private var mockAdapter: MockAdapter? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mockAdapter = MockAdapter()
//...
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recyclerView: RecyclerView = view.findViewById(R.id.recycler_view)
recyclerView.layoutManager = LinearLayoutManager(this.context)
recyclerView.adapter = mockAdapter
}
override fun onDestroyView() {
super.onDestroyView()
mockAdapter = null
}
}
|
Reference#