[TIL] Base Fragment

박봉팔·2024년 3월 6일
0

_binding

기존에 Fragment를 선언하며 메모리 누수를 방지하기위해 바인딩 객체를 _bindingbinding의 형태로 만들어 onCreateView에서 _binding객체를 선언하고 get() = _binding을 통해 바인딩 객체를 가져와 사용하도록 했다.

그리고 onDestroyView단계에서 _binding객체를 null로 대입해 줌으로써 메모리 누수를 방지 할 수 있는데 이를 짧게 설명해보면 다음과 같다.

Fragment 1에서 Fragment 2로 화면이 전환될 경우 onDestroyView를 통해 화면의 View객체들은 사라지지만 onDestroy는 실행되지 않기 때문에 Fragment 1의 바인딩 객체는 살아있는 상태가 된다.


이때 만약 Fragment 1의 코드에서 LiveData를 옵저빙 하고 있으며 LifeCycleOwnerviewLifeCycleOwner가 사용되어 Fragment 1과 생명주기를 함께하게 된다면 Fragment 1은 화면에 없음에도 불구하고 LiveData가 업데이트 될때마다 해당 업데이트를 받게된다.
즉, 메모리 누수가 일어나게 된다.


그렇기 때문에 onDestroyView단계에서 바인딩 객체를 null로 바꿔줌으로써
현재 사용하지 않는 Fragment 1LifeCycle를 비활성화된 상태로 바꿔 메모리 누수를 막는 것이다.


BaseFragment

하지만 위의 방법을 모든 Fragment마다 똑같이 사용하는 것은 불필요한 반복이라 느껴졌고,
해당 코드들을 상속해서 사용할 수 있도록 바꿔주었다.

우선 Fragment마다 해당 코드를 실행하도록 하려면, 그 코드를 가지고 있는 부모 클래스가 필요했기 때문에 Fragment클래스를 상속받는 Base Fragment를 작성했다.

이 경우 어떠한 바인딩 객체를 받더라도 inflate해서 사용할 수 있어야 했기 때문에 제네릭을 사용했다.

// Inflate 타입의 변수를 지정 이후에 (...)의 변수를 받아서 실행한다.
typealias Inflate<T> = (LayoutInflater, ViewGroup?, Boolean) -> T

// inflate로 바인딩Fragment의 inflate함수가 들어온다.
abstract class BaseFragment<VB : ViewBinding>(private val inflate: Inflate<VB>) : Fragment() {
    private var _binding: VB? = null
    val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
    	// 들어온 inflate함수에 Inflate<T> 가 실행된다
        _binding = inflate.invoke(inflater, container, false)
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

상속받아서 사용하기

이제 어떤 Fragment에서든 Base Fragment를 상속받아서 사용하면 위의 코드를 따로 입력하지 않아도 메모리 누수를 방지할 수 있게된다.

// 사용되는 fragment의 타입을 명시해주고 ::inflate를 통해 해당 바인딩의 inflate메서드를 참조하도록 보내준다.
class Fragment1 : BaseFragment<Fragment1>(Fragment1Binding::inflate) { ... }

오늘은 어땠나요?

살아... 남아요... 우리...

profile
개발 첫걸음! 가보자구!

0개의 댓글