-
프래그먼트 장단점
프래그먼트 장점
- 퍼포먼스
- Activity보다 상대적으로 가볍다
- Activity내에서 Fragment는 상대적으로 가볍게 추가/제거가 가능하다. Fragment 백스택에서 Fragment를 관리하는 것이 메모리 관리면에서도 효율적이며, 화면 전환도 Activity보다 더 순조롭게 할 수 있다.
- 유연한 UI/UX 구현
- Fragment는 기본적으로 큰 화면에서 역동적이고 유연한 UI 디자인을 지원하는 것이 목적이었다.
- NavigationDrawer, BottomSheetDialog, Navigation Component 등을 구현할 때도 사용된다.
- 재사용성의 증가
- View or Business Logic을 Fragment 단위로 분리 가능하다.
프래그먼트 단점
비동기로 인해 예기치 않은 동작이 발생할 수 있다.
프래그먼트 수명 주기 처리
프래그먼트의 수명 주기를 관리하는 것은 액티비티의 수명 주기를 관리하는 것과 매우 비슷하다. 액티비티와 마찬가지로 프래그먼트는 세 가지 상태로 존재할 수 있다.
재개됨(Resumed)
프래그먼트가 실행 중인 액티비티에 표시된다.
일시 정지됨(Paused)
다른 액티비티가 포그라운드에 있고 포커스를 갖고 있지만, 이 프래그먼트가 있는 액티비티도 여전히 표시된다.(포그라운드의 액티비티가 부분적으로 투명하거나 전체 화면을 뒤덮지 않음)
정지됨(Stopped)
프래그먼트가 보이지 않는다. 호스트 액티비티가 정지되었거나 프래그먼트가 액티비티에서 제거되었지만 백 스택에 추가된다. 정지된 프래그먼트도 여전히 표시는 된다(모든 상태 및 멤버 정보를 시스템이 보존합니다). 하지만 사용자에게는 더 이상 표시되지 않으며 액티비티를 종료하면 프래그먼트도 종료된다.
액티비티와 마찬가지로 onSaveInstanceState(Bundle), ViewModel 및 영구 로컬 저장소를 결합하여 구성이 변경되고 프로세스가 종료되더라도 프래그먼트의 UI 상태를 보존할 수 있다.
액티비티와 프래그먼트의 수명 주기에서 가장 중대한 차이점은 해당되는 백 스택에 저장되는 방법에 있다. 기본적으로 액티비티는 정지되면 시스템에서 관리하는 액티비티의 백 스택에 들어갑니다(사용자는 Back 버튼을 눌러서 액티비티로 돌아갈 수 있다). 하지만 프래그먼트는 이를 제거하는 트랜잭션에서 addToBackStack()을 호출하여 인스턴스를 저장하라고 명시적으로 요청할 경우에만 호스트 액티비티에서 관리하는 백 스택으로 들어간다.
Fragment 정적 생성 메서드를 쓰는 이유
1. 프래그먼트 재생성시(화면 회전과 같은) 빈 생성자가 있어야 한다.
2. 재생성시 받아온 데이터를 유지하기 위해서 사용한다.
class MainFragment : Fragment(){ override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val index = arguments?.getInt(ARG_INDEX, 0) ?: 0 } companion object { private const val ARG_INDEX = "index" fun newInstance(index: String) = MainFragment().apply { arguments = Bundle().apply { putInt(ARG_INDEX, index) } } } }
newInstance() 방식은 빈 생성자를 만들고 bundle에 데이터를 넣어 처리해 주는데 이렇게 함으로써 화면 회전과 같은 재사용이 되었을 때 데 받아온 데이터를 유지하며 화면에 보여줄 수 있다. 만일 빈 생성자가 없이 사용한다면 could not find Fragment constructor와 함께 InstantiationException이 발생한다.
FragmentFactory
AndroidX부터는 FragmentFactory를 사용하여 인자, 종속성을 가진 프래그먼트를 인스턴스화 할 수 있다.
FragmentFactory를 사용하는 방법
FragmentFactory를 확장하고 FragmentFactory#instantiate()를 재정의한 다음 FragmentManager에 할당한다.
class CustomFragmentFactory(private val dependency: Dependency) : FragmentFactory() { override fun instantiate(classLoader: ClassLoader, className: String): Fragment { if (className == CustomFragment::class.java.name) { return CustomFragment(dependency) } return super.instantiate(classLoader, className) } }
프래그먼트는 FragmentManager에 의해 관리되므로 FragmentFactory를 사용하려면 FragmentManager에 연결해야 한다. Fragment를 포함할 구성 요소 즉 Activity 또는 다른 Fragment의 FragmentManager에 할당해야 한다.
FragmentFactory는 언제 FragmentManager에 할당되어야 할까?
구성 요소(Activity 또는 상위 Fragment) 내에서 Fragment 초기화를 담당하는 FragmentFactory는 Fragment가 생성되기 전에 설정되어야한다. Activity#onCreate() 및 Fragment#onCreate()(이는 Activity 및 Fragment의 기본 클래스)가 호출되기 전에 구성 요소의 FragmentManager에 FragmentFactory를 할당하는 것이 안전하다.
class HostActivity : AppCompatActivity() { private val customFragmentFactory = CustomFragmentFactory(Dependency()) override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = customFragmentFactory super.onCreate(savedInstanceState) // ... } }
class ParentFragment : Fragment() { private val customFragmentFactory = CustomFragmentFactory(Dependency()) override fun onCreate(savedInstanceState: Bundle?) { childFragmentManager.fragmentFactory = customFragmentFactory super.onCreate(savedInstanceState) // ... } }
Fragment와 FragmentFactory를 함께 사용하는 방법
- Fragment tag <fragment> 및 FragmentContainerView를 사용
<?xml version="1.0" encoding="utf-8"?> <androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/customFragment" android:name="com.example.CustomFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:tag="custom_fragment" />
class HostActivity : AppCompatActivity() { private val customFragmentFactory = CustomFragmentFactory(Dependency()) override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = customFragmentFactory super.onCreate(savedInstanceState) setContentView(R.layout.activity_with_fragment_container_view) } }
- FragmentTransaction#add() 메서드를 사용
class HostActivity : AppCompatActivity() { private val customFragmentFactory = CustomFragmentFactory(Dependency()) override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = customFragmentFactory super.onCreate(savedInstanceState) setContentView(R.layout.activity_with_empty_frame_layout) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .add(R.id.content, CustomFragment::class.java, arguments) .commit() } } }
References
'Android' 카테고리의 다른 글
data binding : providing values in Include layout (0) 2021.11.05 WorkManager(1) (0) 2021.10.04 Replace findViewById with View Binding (0) 2021.08.06 ANR(Application Not Responding) (0) 2021.07.19 ViewPager2 (0) 2021.07.10