본문 바로가기

안드로이드 Android

안드로이드 개발 (29) Fragment에서 ViewBinding 사용 시 주의할 점

1. ViewBinding 

빌드 속도가 빠르고 안전하고 코드가 이뻐지는 ViewBinding

ViewBinding 은 xml를 자동으로 바인딩 클래스로 생성해서 xml의 View를 안전하게 사용할 수 있습니다.

kotlin extension deprecated 이 되고나서 요즘은 ViewBinding, DataBinding을 위주로 사용하는 추세이기도 합니다. Fragment에서 ViewBinding 사용시 주의할 점이 한가지가 있는데 이에 대해 정리를 해보도록 하겠습니다.

 

2. Fragment 의 LifeCycle

Fragment 는 onCreateView에서 View를 생성하기 때문에 2개의 LifeCycle이 존재합니다.  

Fragment LifeCycle, Fragment View LifeCycle 이 두 가지가 있습니다.

 

onDestortView()가 발생했을 때 onCreateView()가 다시 호출될 수 있음.

Fragment LifeCycle: onAttach ~ onDetach

Fragment View LifeCycle: onCreateView ~ onDestoryView

 

기본적으로 Fragment 는 Fragment View 보다 LifeCycle이 깁니다. 

 

 

3. Fragment 의 재사용 대비

Fragment는 Fragment의 재사용을 대비해서 Fragment는 View들을 메모리에 보관하도록 되어 있습니다. 

그래서 Fragment의 onDestory가 호출 되고 나면 Fragment에 대한 레퍼런스는 존재하지 않지만 내부적으로 View들은 재사용을 위해 보관하고 있습니다.

 

 

4. 메모리 누수 위험 막기

https://developer.android.com/topic/libraries/view-binding#fragments

 

뷰 결합  |  Android 개발자  |  Android Developers

뷰 결합 기능을 사용하면 뷰와 상호작용하는 코드를 쉽게 작성할 수 있습니다. 모듈에서 사용 설정된 뷰 결합은 모듈에 있는 각 XML 레이아웃 파일의 결합 클래스를 생성합니다. 바인딩 클래스의

developer.android.com

    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
    }

 

위는 구글 공식 샘플 입니다. onDestoryView 함수에서 _binding 객체를 null 만드는것을 볼 수 있습니다. 

Fragment의 생명주기는 view보다 더 길기 때문에 Fragment 의 LifeCycle에 따라, view보다 Fragment가 더 오래 유지됩니다.

https://wooooooak.github.io/android/2019/08/05/fragment%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0%EC%99%80%EA%B0%9D%EC%B2%B4%ED%8C%8C%EA%B4%B4%EC%8B%9C%EC%A0%90/

 

Fragment 메모리 누수에 유의하자. 특히 liveData! · 쾌락코딩

Fragment 메모리 누수에 유의하자. 특히 liveData! 05 Aug 2019 | fragment memory_leak 프래그먼트의 복잡한 lifecycle 프래그먼트는 복잡한 lifecycle을 가지고 있다. 따라서 여러 프래그먼트를 사용하는 앱에서는

wooooooak.github.io

위 링크를 보면 Fragment 객체가 얼마나 오래 유지되는지 알 수 있습니다.

 

그래서 onDestoryView() 함수에서 직접 binding 객체를 null 로 만들어서 gc에서 수집해가도록 해야 메모리 누수를 방지할 수 있습니다.

공식문서의 Note

 

5. 반복 코드 제거

각각 프레그먼트마다 항상 onDestoryView 함수에 binding = null 을 일일히 반복적으로 코드를 작성해야하는 것은 다들 원치 않는 일 일겁니다. 이를 피하기 위해 다른 대안들이 존재합니다. 

 

대안 1) BaseFragment 를 만들어서 상속해서 사용하기

 

BaseFragment.kt

typealias Inflate<T> = (LayoutInflater, ViewGroup?, Boolean) -> T

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? {
        _binding = inflate.invoke(inflater, container, false)
        return binding.root
    }

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

 

class HomeFragment() : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.homeText.text = "Hello view binding"
    }
}

출처: https://stackoverflow.com/questions/64819181/how-to-make-basefragment-with-view-binding

 

How to make BaseFragment with View Binding

My BaseFragment: abstract class BaseFragment : Fragment() { protected abstract val viewModel: ViewModel private var _binding: Bindin...

stackoverflow.com

BaseFragment 를 따로 만들어서 반복되는 코드를 미리 제거할 수 있는 방법 입니다. 가장 많이 사용하는 방법이지 않을까 싶습니다.

 

 

대안 2) kotlin Delegated properties 사용하기 

 

- 구글 샘플앱에 포함된 AutoCleardValue 사용하기

https://github.com/android/architecture-components-samples/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/util/AutoClearedValue.kt

 

GitHub - android/architecture-components-samples: Samples for Android Architecture Components.

Samples for Android Architecture Components. . Contribute to android/architecture-components-samples development by creating an account on GitHub.

github.com

안드로이드 공식문서와 달리 구글 샘플 앱의 소스 코드에서는 AutoCleardValue를 만들어서 위임 패턴으로 사용합니다.

binding 객체에 Delegated properties 를 사용해서 아래와 같이 사용합니다.

  
private var binding by autoCleared<FragmentUserProfileBinding>() 

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

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

binding.txtMain.text = "Hello World!" 
}

 

 

대안 3)  라이브러리 사용

https://github.com/kirich1409/ViewBindingPropertyDelegate

 

GitHub - kirich1409/ViewBindingPropertyDelegate: Make work with Android View Binding simpler

Make work with Android View Binding simpler. Contribute to kirich1409/ViewBindingPropertyDelegate development by creating an account on GitHub.

github.com

 

라이브러리를 사용하는 방법도 존재합니다. 

 

위 방법들 말고도 검색을 하면 할수록 반복 코드를 줄이기 위한 다양한 방법들이 많았습니다. 어떤 방식을 선택할지는 개발자의 판단인 것 같다는 생각이 듭니다. 이상 Fragment에서 ViewBinding을 사용할 때 주의할점에 대해 적었습니다.