조각, ViewBinding 및 메모리 누수

Android 엔지니어로서 해야 할 기본 작업 중 하나는 보기(XML로 작성됨)를 Kotlin/Java 코드와 바인딩하는 것입니다. ButterKnife 과 같은 라이브러리를 사용하거나 Kotlin Android Extensions와 같은 컴파일러 플러그인을 사용하거나 Android Studio/AGP 3.6ViewBinding부터 시작하여 기본 프리미티브( findViewById() )로 이 작업을 수행할 수 있습니다. 몇 가지 다른 옵션이 있지만 내 경험으로는 이것이 가장 일반적입니다.

Kotlin Android Extensions 플러그인은 곧 deprecated(@Parcelize 기능 제외) ViewBinding을 선호합니다. 저는 이것이 긍정적인 변화라고 생각하지만 모든 사람이 제 의견을 공유하는 것은 아닙니다. ViewBinding에 대한 일반적인 주장 중 하나(티켓의 의견, reddit에 대한 토론 및 내 친구에 따르면)는 다음과 같습니다.

ViewBinding introduces memory leaks in Fragments.



공식 문서에서 사용 예를 살펴보겠습니다.

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
}



프래그먼트는 보기보다 오래 지속됩니다. 즉, onDestroyView에서 바인딩 참조를 지우는 것을 잊으면 메모리 누수가 발생합니다. 이제 나는 이것이 오류가 발생하기 쉽다는 데 동의합니다. 생성하는 각 조각에서 바인딩 참조를 지우는 것을 기억해야 합니다. 그러나이 문제는 ViewBinding에 내재된 것이 아니라 이러한 종류의 사용에 내재되어 있습니다. 여기서 문제는 범위가 더 큰 구성 요소(프래그먼트)가 범위가 더 작은 구성 요소(바인딩)에 대한 참조를 유지한다는 것입니다.

참조를 지우는 것이 해결 방법이며 올바른 해결 방법은 참조를 올바른 범위로 이동하는 것입니다.

private val viewModel by viewModels<ProfileViewModel>()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val binding = ResultProfileBinding.bind(view)

    TODO("Set any (click) listeners")

    viewModel.liveData.observe(viewLifecycleOwner) { data ->
        TODO("Bind the data to views")
    }
}



바인딩 참조를 Fragment 범위에서 함수 범위로 이동하면 함수 범위가 뷰 범위보다 작기 때문에 지우는 것에 대해 걱정할 필요가 없습니다. 이제 이것은 UI 계층에 대한 MVVM 패턴을 가정합니다.



내가 본 ViewBinding에 대한 또 다른 주장은 코드가 더 많다는 것입니다. 그냥 binding.view 대신 view를 입력해야 합니다(kotlin Android 확장 사용).

// kotlin-android-extensions
viewModel.liveData.observe(viewLifecycleOwner) { data ->
    textView.text = data.title
    imageView.load(data.imageUrl)
}

// view-binding
val binding = ResultProfileBinding.bind(view)

viewModel.liveData.observe(viewLifecycleOwner) { data ->
    binding.textView.text = data.title
    binding.imageView.load(data.imageUrl)
}



여기서 고통을 덜기 위해 kotlin의 범위 지정 기능with을 사용할 수 있습니다.

// view-binding
val binding = ResultProfileBinding.bind(view)

viewModel.liveData.observe(viewLifecycleOwner) { data ->
    with(binding) {
        textView.text = data.title
        imageView.load(data.imageUrl)
    }
}



반복binding이 사라졌습니다. 조회수가 많을수록 확장성이 좋아집니다. with(binding)는 한 번만 작성하면 됩니다.

결론



조각과 함께 ViewBinding을 사용하면 오류가 발생하기 쉬울 수 있지만 반드시 그럴 필요는 없습니다. 해결 방법은 Fragment 범위에서 로컬 함수 범위로 바인딩에 대한 참조를 이동하는 것입니다.

좋은 웹페이지 즐겨찾기