7. 궁금했던 것들 2편 - 바인딩 클래스와생명 주기
궁금했던 것 2편이다. 오늘은 궁금했던 것들 1편 - View Binding의 성능 향상 글의 연장선상에 있는 이야기를 하려고 한다. View Binding에 관련된 얘기다. View Binding에 대한 설명은 위의 글에서 설명해놓았으니 궁금하다면 보고 오자.
Activity - Fragment에서 View Binding 구현사항의 차이
View Binding은 Activity, Fragment 모두에게 적용할 수 있다. 바인딩 클래스 인스턴스를 할당한 binding 변수를 통해 사용하게 된다.
activity에서 view binding 적용 코드
private lateinit var binding: ResultProfileBinding
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = ResultProfileBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
}
fragment에서 view binding 적용 코드
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
확연히 다르다. fragment에서 var binding 변수는왜 onCreateView~onDestroyView 주기 사이에서만 유효한 것일까? activity는 그런 제약이 없는데. 그리고 소름돋게도, 안드로이드 공식 문서는 바로 밑에서 이에 대해 설명한다.
참고 : 프래그먼트는 뷰보다 오래 지속됩니다. 프래그먼트의 onDestroyView() 메서드에서 결합 클래스 인스턴스 참조를 정리해야 합니다.
더 소름돋는 것은, 읽어도 잘 모르겠다.
Fragment의 수명 주기?
인터넷에 해답은 아주아주 많겠지만, 한 번 둘러가보려고 한다. 먼저 Fragment의 수명 주기에 대해 검색해봤다.
먼저 프래그먼트는 뷰보다 오래 지속된다는 것은 이해가 됐다. 따라서 onCreateView()에서 바인딩 변수에 인스턴스를 할당해준 다음, onDestroyView()에서 바인딩 변수 값을 null로 주며 view lifecycle과 맞춰준다는 것이다.
하지만 하나의 의문이 더 생긴다. 프로젝트에서 해당 코드
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
를 지워도 잘 동작한다는 것이다. 동작엔 문제가 없다면, 잠깐이나마(onDestroy) 메모리 누수가 발생하기 때문에 해당 코드를 적어주는 건가? activity에서는 해당 코드를 적용하라는 가이드가 없었는데. 단순히 View의 생명주기와 맞춰주기 위해 지워준다는 것인지, 아니면 내가 모르는 뭔가가 있는지 궁금했다. 그리고 액티비티 수명주기에선 onDestroyView()라는 콜백이 없었는데 말이다.
Fragment Lifecycle의 특성
일단 Fragment는 고유한 생명주기를 가지고 있으면서도, View와 관련된 생명주기를 가지고 있다. Activity의 Lifecycle과 다르게 onCreateView(), 그리고 onDestroyView()라는 콜백이 따로 존재한다. 이러한 이유는 프래그먼트가 액티비티 상에서 동작하기 때문이다. onCreate()와 onDestroy()는 프래그먼트가 생성되고 소멸될 때 발생하는 콜백, 그리고 onCreateView()와 onDestroyView()는 뷰가 구성되고 지워질 때 발생하는 콜백이다. 이 부분에서 Activity의 Lifecycle과 크게 다르다. 따라서 화면 구성에 관련된 생명주기는 onCreateView()와 onDestroyView()이다. 일단 하나는 이해된다. 바인딩 클래스는 애초에 layoutInflater를 인자로 넣는, 화면 구성에 관련된 클래스이기 때문에. 프래그먼트에서 해당 인스턴스가 살아있는 시간이 onCreateView()부터 onDestroyView() 사이가 된다는 것은 당연한 얘기인 것 같다.
Fragment replace 동작 이후 상황
그렇다면, onDestroyView()에서 해당 인스턴스를 null 처리하지 않았는데도 정상 작동이 된 이유는 무엇일까? 메모리 누수가 발생한 걸까. 그리고 해당 질문에 대한 해답을 찾았다. Fragment는 하나의 Activity 상에서 replace되며 작동한다. replace를 한 순간, 이전 프래그먼트의 모든 View 요소들은 제거되지만, 프래그먼트 클래스의 인스턴스들은 그대로 backstack에 저장된다고 한다. 여기서 새롭게 교체된 프래그먼트와 관계없는 인스턴스들이 정리되지 않아 있다면, Memory Leak(메모리 누수)의 가능성이 생기는 것이다. 따라서 결과적으로는 View Lifecycle과 클래스 인스턴스의 정리를 위해 해당 처리(binding = null)를 해주는 것이었다.
결론
Activity, Fragment의 구현 사항이 다르다는 점에서 시작해서, 프래그먼트에선 바인딩 클래스의 유효 범위가 있다는 것을 알게되었고, 이는 Fragment Lifecycle의 특성으로 인해 View LifeCycle을 고려한 것, 그리고 인스턴스가 죽지 않고 Memory Leak을 발생시킬 수 있기 때문에 이러한 구현 사항 차이가 발생했다는 것을 알게 되었다. 앞으로도 안드로이드 개발을 진행하며 프래그먼트와 더욱 친해져야 할텐데, 이러한 정보들을 알게 되어 상당히 기쁘다.
Author And Source
이 문제에 관하여(7. 궁금했던 것들 2편 - 바인딩 클래스와생명 주기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@dongwan999/6.-궁금했던-것들-2편-바인딩-클래스와생명-주기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)