Fragment (프래그먼트)
프래그먼트는 FragmentActivity 내의 어떤 동작 또는 사용자 인터페이스의 일부를 나타낸다.
또한, 프래그먼트는 액티비티의 모듈식 섹션이라고 생각하면 된다.
이는 자체적인 수명 주기를 가지고, 자체 입력 이벤트를 수신하고, 액티비티 실행 중에 추가 및 삭제가 가능하다.
프래그먼트는 항상 액티비티 내에서 호스팅되어야 하며, 해당 프래그먼트의 수명 주기는 호스트 액티비티의 수명 주기에 직접적으로 영향을 받는다.
ex. 액티비티가 일시정지되는 경우, 그 안의 모든 프래그먼트도 일시정지되며 액티비티가 소멸되면 모든 프래그먼트도 모두 소멸.
프래그먼트는 액티비티와 다르게 조금은 다른 생명주기를 가지고 있다.
프래그먼트의 고유한 생명 주기를 가지고 있으면서, View 와 관련된 생명 주기도 가지고 있다.
즉, 2개의 라이프 사이클이 존재한다.
- Fragment LifeCycle
- Fragment View LifeCycle
위의 그림을 보면 프래그먼트 수명 주기 처리를 그림으로 나타내 있다.
프래그먼트 콜백 쪽 보면, onCreateView() 와 onCreate() 두 가지의 생명 주기가 있는 걸 볼 수 있다.
onCrate() : 프래그먼트가 생성될 때, 즉 화면이 아직 보이지 않는 상태에서 실행 됨.
=> ex. Fragment LifeCycle : onAttach() ~ onDetach()
onCreateView : 화면을 구성할 때 호출되는 부분
=> ex. Fragment View LifeCycle : onCreateView() ~ onDestroyView()
따라서, 직접적으로 화면과 관련된 생명주기는 OnCreateView() 와 onDestroyView 이다.
Fragment 의 재사용
프래그먼트는 재사용을 대비해서 프래그먼트의 View 들을 메모리에 보관하도록 되어있다.
추가로 설명하면, 프래그먼트 A에서 B로 화면이 바뀌게될 때, 프래그먼트 A의 모든 View 요소들은 제거가된다.
하지만, 프래그먼트의 instance는 그대로 남아서 backStack에 저장될 것이다.
ex.) 프래그먼트에 네비게이션 컴포넌트, 백스택 또는 디테치를 사용할 경우, onDestroyView() 이후에 Fragemnt View 는 종료되지만, Fragment 는 여전히 살아있다.
다시 말해서,
프래그먼트의 onDestroy() 호출되고 나면 프래그먼트에 대한 참조는 더 이상 존재하지 않지만, 내부적으로 View들을 재사용하기 위해 보관하게 된다는 것이다.
여기서 메모리 릭의 가능성이 생긴다.
Fragment 에서 메모리 누수 방지
아래 코드는 구글 문서에 나온 예제입니다.
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? {
// inflate() 메서드 호출 : Fragment 에서 사용할 결합 클래스 인스턴스 생성이 됨.
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root // 루트 뷰 참조 가져오기
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
위 코드를 보게되면, onDestroyView() 가 호출될 시, _binding 객체를 null 처리를 해준 걸 확인할 수 있다.
저 코드는 ViewBinding 에 대한 참조를 가비지 컬렉터가 가비지 컬렉션을 할 수 있도록 하는 코드이다.
그리고 기존 액티비티 코드처럼 단순하게 lateinit 으로 선언했다면 onDestroyView 에서 binding을 null로 초기화할 수가 없다.
따라서, _binding을 선언한 후, nullable 처리를 해줬고, binding 프로퍼티에 가져올 때 null 일 경우 예외를 발생시키기 위해 예외 연선자 (!!)를 사용한 코드다.
이제 결합 클래스 인스턴스를 사용하여 뷰를 참조할 수 있다.
binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }
참고
https://developer.android.com/topic/libraries/view-binding?hl=ko
https://developer.android.com/guide/components/fragments?hl=ko
https://gift123.tistory.com/58
https://yoon-dailylife.tistory.com/57
'Mobile App' 카테고리의 다른 글
[안드로이드 에러] Firebase not working com.google.firebase.installations.FirebaseInstallationsException (0) | 2022.08.11 |
---|---|
[안드로이드 에러] End of input at line 1 column 1 path $ (0) | 2022.07.29 |
[안드로이드] - Data Layer (데이터 레이어) (0) | 2022.07.26 |
[안드로이드] - 도메인 레이어 (Use Case) (0) | 2022.07.25 |
[안드로이드] - UI Layer Architecture (0) | 2022.07.23 |