쾌락코딩

Fragment 메모리 누수에 유의하자. 특히 liveData!

|

프래그먼트의 복잡한 lifecycle

프래그먼트는 복잡한 lifecycle을 가지고 있다. 따라서 여러 프래그먼트를 사용하는 앱에서는 메모리 누수에 조심해야 한다.

우선 프래그먼트의 라이프사이클 그림을 보자.

image

각 프래그먼트의 역할은 공식문서에 잘 나와있기 때문에 따로 설명하지는 않지만 특별히 눈여겨 봐야할 메서드가 있다. 바로 onDestroyView()onDestroy()다.

onDestroyView()onDestroy()

onDestroyView()가 호출되면 프래그먼트 객체 자체는 사라지지 않고 메모리에 남아있다. 반면 onDestroy()가 호출되면 프래그먼트 객체가 파괴된다. 프래그먼트들을 계속 바꿔가며 사용할 경우를 생각해보자.

예를 들어 아래와 같이 하단 탭바에 여러개의 메뉴를 두고 각각의 프래그먼트를 생성하여 앱이 동작하는 경우 특별히 신경써주지 않으면 메모리가 누수되고 있다는 사실을 모르고 있을 확률이 높다.

image

앱을 켠 직후 1번 탭에 들어갔다고 가정하자. 일반적인 fragment 라이프사이클에 따라 프래그먼트 객체가 생성되고 view가 보여진다. 이 경우 onAttach()부터 onResume()까지가 호출되어 있다. 1번 프래그먼트 객체 주소값은 A1A1A1이다

그리고 이제 2번 탭을 눌러 2번 프래그먼트로 이동하자. 2번 객체가 생성되기 전에 1번 프래그먼트에서 onDestroyView()가 호출된다. 그러나 onDestroy()는 호출되지 않는다. 즉, 1번 프래그먼트 객체가 제거되지는 않았다는 뜻이며 오로지 1번 프래그먼트의 view만 파괴되었다는 뜻이다. 1번 뷰가 사라지고 2번 프래그먼트의 객체와 view가 라이프사이클에 따라 생성된다. 2번 프래그먼트 객체 주소값은 B2B2B2이다.

현재 우리는 2번 프래그먼트를 보고있는 상태다. 그러나 여전히 1번 프래그먼트의 객체(A1A1A1)는 살아있다.

문제점은 바로 이 지점이다. 현재 2번 프래그먼트에서 사용자와 상호작용 중인데, 1번 객체는 여전히 살아있다. 만약 1번 객체에서 LiveData를 구독하고 있다고 생각해보자. 프래그먼트의 라이프사이클 인지를 위해 lifecyclerOwner에다가 아무 생각 없이 this(프래그먼트 객체)를 바인딩 하고 있는가?

만약 그렇다면 1번 프래그먼트의 liveData는 사용자가 2번, 3번 프래그먼트에 머무르고 있을때도 지워지지 않고 존재하고 있는 셈이다.

메모리 누수인 셈이다.

LiveData는 lifecycleOwner가 DESTROYED 되어야 사라지기 때문이다.

onDestroy가 호출되는 시점

2번 메뉴에 머물다가 다시 1번 프래그먼트로 돌아갈 때 라이프사이클을 보자.

1번으로 돌아가는 순간 아래와 같은 라이프사이클이 호출된다.

  1. onCreate() 호출 : A2A2A2
  2. onCreateView() 호출 : A2A2A2
  3. onStart() 호출 : A2A2A2
  4. onResume() 호출 : A2A2A2
  5. onDestroy() 호출 : A1A1A1

마지막 부분에, 이전에 까지 유지되고 있던 1번 프래그먼트의 객체 A1A1A1가 파괴된다.

결론

프래그먼트에서 메모리 누수를 막기위해서 lifecycleOwner로 viewLifecycleOwner를 고려하자.

Comments