ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Fragment
    Android 2021. 8. 16. 11:28

    프래그먼트 장단점

    프래그먼트 장점

    • 퍼포먼스

     - 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
Designed by Tistory.