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에 대한 공식 문서에 나와있는 내용이다.
프래그먼트는 뷰보다 오래 살아있기 때문에 onDestroyView 메서드에서 binding 클래스 인스턴스를 정리해야 한다는 것이다.
onDestroyView 메서드가 호출될 때 fragment의 뷰는 destroy되지만 객체 자체는 사라지지 않고 메모리에 남아있다.
그렇기에 garbage collector는 binding 프로퍼티가 살아있다고 판단하기 때문에 binding에 대한 메모리를 해제하지 않게 된다.
그래서 onDestroyView 메서드에서 직접적으로 _binding 프로퍼티를 null로 만들어서
garbage collector가 _binding 프로퍼티가 더 이상 사용하지 않는 것임을 판단하게 만들어 수집해 가도록 만드는 것이다.
또한, nullable한 _binding과 non-null 한 binding 프로퍼티 2개를 사용하는 이유는,
만약 한 개의 binding 프로퍼티를 사용한다면 onDestroyView에서 binding을 null로 설정하기 위해 nullable 한 binding 프로퍼티를 만들어야 한다.
이후 nullable한 프로퍼티이기 때문에 이를 사용하기 위해서 항상 null check를 하거나 safe call을 통해 접근해야 한다.
이를 좀 더 편하고 직관적으로 사용하기 위해 2개의 프로퍼티를 두어 사용하는 것이다.
하지만 이렇게 사용할 때에도 주의할 점은 있다.
공식 문서에 나와있듯이
non-null 한 binding 프로퍼티는 onCreateView와 onDestroyView 사이에서만 유효하다.
따라서 이 점을 유의해서 잘 사용해야 한다.
'안드로이드' 카테고리의 다른 글
[안드로이드] RxJava3CallAdapterFactory (0) | 2023.03.20 |
---|---|
[안드로이드] Retrofit 분석 (0) | 2023.03.11 |
[Android] Fragment Lifecycle (0) | 2023.02.12 |
[안드로이드] ListAdapter (0) | 2023.02.10 |