[Android] 개념 공부

hyejeong·2024년 4월 1일

[Android]

목록 보기
1/3

Activity life Cycle 활동 수명 주기

  • activity 클래스는 상태가 변경되거나 시스템이 활동을 생성, 중지 또는 재개하거나 활동이 있는 프로세스를 삭제할 때 이를 활동에 알리는 여러 콜백을 제공함.
  • 사용자가 활동을 벗어났다가 다시 돌아왔을 때 활동이 작동하는 방식을 수명 주기 콜백 메서드에서 선언 가능함.
    • 예 - 스트리밍 동영상 플레이어를 빌드하는 경우 사용자가 다른 앱으로 전환할 때 동영상을 일시중지하고 네트워크 연결을 종료한다면 → 사용자가 돌아오면 네트워크에 다시 연결하고 사용자가 같은 지점에서 동영상을 재개할 수 있도록 할 수 O.

수명 주기 콜백이 잘되지 않는다면 발생 가능한 문제들 :

  • 앱을 사용하는 중에 전화가 오거나 다른 앱으로 전환 할 때 비정상 종료.
  • 앱을 활발하게 사용하지 않는 경우 귀중한 시스템 리소스가 소비됨.
  • 앱에서 나갔다가 나중에 들어왔을 때 사용자의 진행 상태가 저장되지 않음.
  • 화면이 가로 방향과 세로 방향 간에 전환할 경우, 비정상 종료되거나 사용자의 진행 상태가 저장되지 않음.

활동 수명 주기 개념

  • 단계 간 전환을 탐색하기 위해 Activity 클래스는 6가지 콜백으로 구성된 핵심 집합인 onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy()를 제공함.
  • 앱은 백그라운드에서 실행될 때 활동을 시작할 수 없음.

onCreate()

  • 시스템이 먼저 활동을 생성할 때 실행되는 것으로, 필수 구현. 활동이 생성되면 생성된 상태가 됨. onCreat() 메서드에서 활동의 전체 수명 주기 동안 한 번만 발생하는 기본 애플리케이션 시작 로직을 시작함.

onRestart()

  • 액티비티가 멈췄다가 다시 시작되기 바로 전에 호출됨.

onStart()

  • 액티비티가 사용자에게 보여지기 바로 직전에 호출됨.
  • = 활동이 시작됨 상태로 전환되면 시스템은 onStart() 를 호출함.
  • 활동이 시작됨 상태로 유지되지 않고, 콜백이 완료되면 활동이 재개됨 상태로 전환되고 시스템은 onResume() 메서드를 호출함.

onResume()

  • 활동이 → “재개됨” : 포그라운드로 이동하고 시스템에서 onResume() 콜백을 호출함. 이때 앱이 사용자와 상호작용함.
  • = 액티비티가 사용자와 상호작용하기 바로 전에 호출됨.
  • 앱에서 포커스를 잃을 때까지 이 상태 유지함.
  • 활동이 → “재개됨” : 활동의 수명 주기와 연결된 모든 수명 주기 인식 구성요소는 on_Resume 이벤트를 수신함.
  • 활동이 “일시중지” → “재개됨” : 시스템은 onResume() 메서드를 다시 한번 호출함. 따라서 onResume() 를 구현하여 onPause() 중에 해제하는 구성요소를 초기화하고 활동이 “재개됨” 상태로 전환될 때마다 발생해야 하는 다른 초기화를 실행함.

onPause()

  • 사용자가 활동을 떠난다는 첫 번째 신호로 이 메서드를 호출함 → 항상 활동이 소멸된다는 의미X. = 더 이상 포그라운드에 있지 않음을 나타내지만 사용자가 멀티 윈도우 모드에 있느 경우에는 계속 표시됨.
  • 활동이 pause 전환되는 이유
    • 멀티 윈도우 모드에서는 언제나 하나의 앱만 포커스를 가지며 시스템이 다른 모든 앱을 일시중지함.
  • = 다른 액비티비가 보여질 때 호출됨. 데이터 저장, 스레드 중지 등의 처리를 하기에 적당한 메소드.
  • 활동이 일시중지됨 상태로 전환되면 모든 수명 주기 인신 구성요소는 on_pause 이벤트를 수신함.

onStop()

  • 활동이 사용자에게 더 이상 표시되지 않으면 “중지됨” 상태로 전환되고 시스템에서 onStop() 콜백을 호출함.
  • 액티비티가 더이상 사용자에게 보여지지 않을 때 호출됨.
  • 메모리가 부족할 경우에는 onStop() 메소드가 호출되지 않을 수 있음.

onDestroy()

  • 액티비티가 소멸되기전에 호출됨.
  • = finish() 메소드가 호출되거나 시스템이 메모리 확보를 위해 액티비티를 제거할 때 호출됨.
  • 콜백 호출 이유
    1. 사용자가 활동을 완전히 닫았기 때문에 활동에서 finish() 가 호출되어 활동이 종료되고 있는 것.
    2. 기기 회전 또는 멀티 윈도우 모드 전환과 같은 구성 변경으로 인해 시스템이 일시적으로 활동을 소멸하는 경우
  • 활동이 소멸됨 상태로 전환하면 ON_DESTROY 이벤트를 수신함.
  • 액티비티가 다시 생성되지 않으면 ViewModel에는 onCleared() 메서드가 호출되어 소멸되기 전에 필요한 모든 데이터를 정리할 수 O.

Activity Stack

  • 태스크는 어플리케이션 실행시 액티비티를 기록하는 스택.
  • 스택에 각 액티비티가 열리는 순서로 정렬됨
  • 액티비티를 여러개 호출 시 스택 쌓임.
  • 스택에서 맨 위의 액티비티가 화면에 보임.

standard

  • 후입선출 (LIFO, Last In First Out)
  • 호출하는 모든 액티비티가 위에 쌓임.

singleTop

  • 가장 위의 액티비티는 중복해서 쌓지 않음.

singleTask

  • 쌓여있던 스택은 남아있고, 새로운 스택 공간이 하나 더 생김.

singleInstance

  • 쌓여있던 스택은 남아있고, 새로운 스택 공간이 하나 더 생김.
  • 새로 생긴 스택 공간에는 다른 액티비티 스택이 쌓이지 않음.
  • 다른 액티비티가 호출되면 기존에 있던 스택 공간에 쌓임.
  • 한 태스크의 하나의 singleInstance 액티비티만 존재 가능.


fragment lifeCycle

  • 각 fragment 인스턴스에는 고유한 수명 주기가 있음.
  • 사용자가 앱을 탐색하거나 앱과 상호작용 할 때 프래그먼트는 화면에서 추가 또는 삭제되거나 화면에 들어가고 나오면서 수명 주기의 여러 상태간에 전환하게 됨.
  • 수명 주기를 관리하기 위해 Fragment는 LifecycleOwner 를 구현하여, getLifecycle()메서드를 통해 액세스 할 수 있는 Lifecycle 객체를 노출함.

생명주기

  • 최상단으로 올라왔을 때 : CREATED - STARTED - RESUMED 순으로 진행.
  • 백스택에서 pop됐을 경우 : RESUMED - STARTED - CREATED - DESTROYED

OnCreate()

  • CREATED 상태라면, 프래그먼트는 FragmentManager 에 추가되고 onAttach() 메서드가 호출된 상태임.
  • onCreate() 이전에 onAttach() 가 먼저 호출됨, 이 시점에서 아직 Fragment View 가 생성되지 않았음.
  • onSaveInstanceState()에 의해 저장된 상태를 포함하는 savedInstanceState 파라미터는 프래그먼트가 처음 생성됐을 때만 null로 넘어오며, onSaveInstanceState() 함수를 재정의하지 않았더라도 그 이후 재생성부터는 non-null 값으로 넘어옴.

onCreateView(), onViewCreated()

  • onCreateView() 의 반환값으로 정상적인 Fragment View 객체를 제공했을 때만 Fragment View의 Lifecycle 이 생성됨.

onViewStateRestored()

  • 저장해둔 모든 state 값이 Fragment의 View의 계층구조에 복원됐을 때 호출됨.
  • 체크 박스 위젯이 현재 체크 되어있는지 등 각 뷰의 상태값을 체크할 수 있음.

onStart()

  • Fragment가 사용자에게 보여질 수 있을 때 호출됨.
  • Fragment의 child FragmentManager를 통해 FragmentTransaction을 안전하게 수행 가능함.
  • Fragment의 Lifecycle이 Started로 이동한 후에 Fragment View 의 Lifecycle 또한 Started로 변환됨.

onResume()

  • Fragment가 보이는 상태에서 모든 Animator와 Transition 효과가 종료됨.
  • 프레그먼트가 사용자와 상호작용할 수 있을 때 onResume() 콜백이 호출됨,
  • Activity 의 onResume() 시점과 유사함.
  • Resumend 상태가 됐다는 것이 사용자가 프래그먼트와 상호작용 하기에 적절한 상태가 됐다고 하는데, onResume()이 호출되지 않은 시점에서는 입력을 시도하거나 포커스르 설정하는 등의 작업을 임의로 함면 안된다는 것을 의미함.

onPause()

  • 사용자가 Fragment를 떠나기 시작했지만 Fragment는 여전히 visible일 때 onPause()가 호출됨.
  • Fragment와 View의 Lifecycle이 Paused가 아닌 Started가 됨.

onStop()

  • Fragment가 더이상 화면에 보여지지 않게 되면 Fragment와 View의 Lifecycle은 Created상택 되고, onStop() 콜백 함수가 호출되게 됨.
  • 부모 액티비티나 프래그먼트가 중단됐을 때 뿐만아니라 부모 액티비티나 프래그먼트의 상태가 저장될 때도 호출됨.
  • API 28버넌을 기점으로 onStop()이 onSaveInstanceState() 함수보다 먼저 호출됨으로써 onStop()이 FragmentTransaction을 안전하게 수행할 수 있는 마지막 지점이 되었음.


onDestroyView()

  • 모든 exit animation 과 transition이 완료되고, Fragment가 화면으로부터 벗어났을 경우 Fragment View 의 Lifecycle의 Destroyed 가 되고 onDestroy()가 호출됨.
  • 가비지 컬렉터에 의해 수거될 수 있도록 Fragment View 에 대한 모든 참조가 제거되야함.

onDestroy()

  • Fragment가 제거되거나 FragmentManager가 destroy 됐을 경우, 프래그먼트의 Lifecycle은 Destroyed 상태가 되고, onDestroy() 콜백 함수가 호출됨.

fragment 데이터 전달 방식

  1. bundle과 FragmentManager로 전달.
  2. Fragment Result API를 사용하여 Data 전달.
  3. Fragment간 공통의 ViewModel 로 전달.
  4. Jetpack의 Navigation에서 제공하는 safe-args 로 전달.

2. Fragment Result API를 사용하여 Data 전달.

  • 예 - QR 코드를 읽고 이전 프래그먼트로 데이터를 다시 전달하는 프래그먼트가 있다면, FragmentManager는 FragmentResultOwer를 구현함, FragmentManager는 프래그먼트 결과의 중앙 저장소 역할을 할 수 있음.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Use the Kotlin extension in the fragment-ktx artifact.
    setFragmentResultListener("requestKey") { requestKey, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported.
        val result = bundle.getString("bundleKey")
        // Do something with the result.
    }
}
  • 결과를 생성하는 프래그먼트인 프래그먼트 B에서 동일한 requestKey를 사용하여 동일한 FragmentManager에 결과를 설정함. 이 작업은 setFragmentResult() API를 사용하여 실행할 수 있음.
button.setOnClickListener {
    val result = "result"
    // Use the Kotlin extension in the fragment-ktx artifact.
    setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}

3. Fragment간 공통의 ViewModel 로 전달.

  • 예 - 목록을 표시하는 프래그먼트 하나와 사용자가 목록에 다양한 필터를 적용할 수 있는 프래그먼트 하나를 생각해보면, 프래그먼트가 직접 통신하면 간단하지만 그러면 더 이상 독립적이지 않음.
  • ViewModel 을 공유하면 프래그먼트는 서로에 관해 알 필요가 없으며 활동은 액티비티는 통신을 용이하게 하기 위해 작업을 할 필요가 없음
class ListViewModel : ViewModel() {
    val filters = MutableLiveData<Set<Filter>>()

    private val originalList: LiveData<List<Item>>() = ...
    val filteredList: LiveData<List<Item>> = ...

    fun addFilter(filter: Filter) { ... }

    fun removeFilter(filter: Filter) { ... }
}

class ListFragment : Fragment() {
    // Using the activityViewModels() Kotlin property delegate from the
    // fragment-ktx artifact to retrieve the ViewModel in the activity scope.
    private val viewModel: ListViewModel by activityViewModels()
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.filteredList.observe(viewLifecycleOwner, Observer { list ->
            // Update the list UI.
        }
    }
}

class FilterFragment : Fragment() {
    private val viewModel: ListViewModel by activityViewModels()
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.filters.observe(viewLifecycleOwner, Observer { set ->
            // Update the selected filters UI.
        }
    }

    fun onFilterSelected(filter: Filter) = viewModel.addFilter(filter)

    fun onFilterDeselected(filter: Filter) = viewModel.removeFilter(filter)
}

4. Jetpack의 Navigation에서 제공하는 safe-args 로 전달.

Jetpack compose Navigation 세팅

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/fragmentA">

    <fragment
        android:id="@+id/fragmentA"
        android:name="com.gunt.fragmentdatapassexample.pass.passargs.fragmentA"
        android:label="fragmentA" >
        <action
            android:id="@+id/fragmentAToB"
            app:destination="@id/fragmentB"
            app:launchSingleTop="true" />
        <argument
            android:name="argsString"
            app:argType="string"
            android:defaultValue=""/>
    </fragment>
    <fragment
        android:id="@+id/fragmentB"
        android:name="com.gunt.fragmentdatapassexample.pass.passargs.fragmentB"
        android:label="fragmentB" >
        <action
            android:id="@+id/passBToA"
            app:destination="@id/fragmentA"
            app:launchSingleTop="true"
            app:popUpTo="@id/nav_graph" />
        <argument
            android:name="argsString"
            app:argType="string"
            android:defaultValue=""/>
    </fragment>
</navigation>
  • 프래그먼트 A
class FragmentA : Fragment(){
 override fun onCreateView(
    //생략 ):
    
     val args: navFragmentA by navArgs()
        binding.textView.text = args.argsString // nav agrument 생성한것

        binding.btnSend.setOnClickListener {
            val action = FragmentADirections.passAToB(binding.etText.text.toString())
            findNavController().navigate(action)
        }
}
  • 프래그먼트 B
class FragmentA : Fragment(){
 override fun onCreateView(
    //생략 ):
    
     val args: navFragmentB by navArgs()
        binding.textView.text = args.argsString // nav agrument 생성한것

        binding.btnSend.setOnClickListener {
            val action = FragmentADirections.passBToA(binding.etText.text.toString())
            findNavController().navigate(action)
        }
}

출처
[Android] 의외로 잘 모르는 Fragment 의 Lifecycle
https://velog.io/@blue-sky/Android-Fragment-to-Fragment-데이터-전달
https://developer.android.com/guide/fragments/lifecycle?hl=ko

0개의 댓글