본문 바로가기

안드로이드

[Android] Fragment ViewBinding 사용 시 주의할 점

 

Fragment에서 ViewBinding을 사용할 시 주의할 점에 대해 알아보자.

 

 

ViewBinding을 사용할 때 Activity와 Fragment에서 binding을 생성하는 코드를 작성해야 한다.

 

하지만 프로젝트의 크기가 커질수록 Activity와 Fragment의 수가 많아지면 바인딩에 대한 boilerplate 코드가 생길 수 있다.

 

그래서 Base 코드들을 작성해 ViewBinding에 대한 코드를 적어놓기도 한다.

 

 

View Binding을 Activity에서 사용할 때

 

abstract class BaseActivity<T : ViewDataBinding>(
    @LayoutRes val layoutRes: Int
) : AppCompatActivity() {

    protected lateinit var binding: T

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, layoutRes)
    }
}

 

 

Activity에서 ViewBinding을 사용할 때에는 lateinit 변수로 binding을 생성하고 onCreate() 메서드에서 binding을 초기화하는 방법을 사용한다.

 

하지만 아래 Fragment에서 ViewBinding을 사용할 때는 Activity와는 조금 다르게 사용할 것을 권장한다.

 

 

View Binding을 Fragment에서 사용할 때

 

abstract class BaseFragment<T : ViewDataBinding>(
    @LayoutRes val layoutRes: Int
) : Fragment() {

    private var _binding: T? = null
    // This property is only valid between onCreateView and
    // onDestroyView.
    protected val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = DataBindingUtil.inflate(
            inflater,
            layoutRes,
            container,
            false
        )
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        // memory leak 방지를 위해 null 처리
        _binding = null
    }
}

 

 

Fragment에서 ViewBinding을 사용할 때에는 lateinit 변수로 binding을 생성하지 않고,

nullable 한 바인딩 프로퍼티(_binding)와, !! 연산자를 사용한 강제로 변환된 non-null 타입의 프로퍼티(binding)를 사용한다.

 

이후 onCreateView 메서드에서 binding layout을 inflate 하고 getRoot 함수를 통해 뷰를 리턴한다. 

 

@NonNull
@Override
public View getRoot() {
    return mRoot;
}

 

또한, onDestroyView 메서드에서 _binding에 null 처리를 해 주는 것을 볼 수 있다.

 

 

왜 Activity에서 사용하는 것과 다르게 nullable, non-null binding 프로퍼티를 2개 사용하고 onDestroyView 메서드에서 _binding을 null 처리해 주는 것일까?

 

Fragment에서 ViewBinding을 사용할 경우 Fragment는 View보다 오래 살아있어 lifecycle 때문에 memory leak이 발생할 수 있기 때문이다.

 

아래는 Fragment Lifecycle과 View Lifecycle에 대한 공식 문서에 나와있는 내용이다.

 

Fragment Lifecycle

 

Fragment Note

 

 

프래그먼트는 뷰보다 오래 살아있기 때문에 onDestroyView 메서드에서 binding 클래스 인스턴스를 정리해야 한다는 것이다.

 

onDestroyView 메서드가 호출될 때 fragment의 뷰는 destroy되지만 객체 자체는 사라지지 않고 메모리에 남아있다.

 

그렇기에 garbage collector는 binding 프로퍼티가 살아있다고 판단하기 때문에 binding에 대한 메모리를 해제하지 않게 된다.

 

그래서 onDestroyView 메서드에서 직접적으로 _binding 프로퍼티를 null로 만들어서

garbage collector가 _binding 프로퍼티가 더 이상 사용하지 않는 것임을 판단하게 만들어 수집해 가도록 만드는 것이다.

 

 

또한, nullable한 _bindingnon-null 한 binding 프로퍼티 2개를 사용하는 이유는,

만약 한 개의 binding 프로퍼티를 사용한다면 onDestroyView에서 binding을 null로 설정하기 위해 nullable 한 binding 프로퍼티를 만들어야 한다.

 

이후 nullable한 프로퍼티이기 때문에 이를 사용하기 위해서 항상 null check를 하거나 safe call을 통해 접근해야 한다.

이를 좀 더 편하고 직관적으로 사용하기 위해 2개의 프로퍼티를 두어 사용하는 것이다.

 

하지만 이렇게 사용할 때에도 주의할 점은 있다.

 

공식 문서에 나와있듯이

non-null 한 binding 프로퍼티는 onCreateView와 onDestroyView 사이에서만 유효하다.

 

view binding in fragments

 

따라서 이 점을 유의해서 잘 사용해야 한다.

 

 

 

 

 

 

'안드로이드' 카테고리의 다른 글

[안드로이드] RxJava3CallAdapterFactory  (0) 2023.03.20
[안드로이드] Retrofit 분석  (0) 2023.03.11
[Android] Fragment Lifecycle  (0) 2023.02.12
[안드로이드] ListAdapter  (0) 2023.02.10