• ViewModelScope

앱의 각 VIewModel을 대상으로 정의된다. 이 범위내에서 시작되는 모든 코루틴들은 ViewModel이 삭제되면 전부 취소된다.
Coroutine이 ViewModel이 살아있는 경우에만 존재해야할 경우에 유용하다.

class MyViewModel: ViewModel() {
    init {
        viewModelScope.launch {
            // Coroutine that will be canceled when the ViewModel is cleared.
        }
    }
}
  • LifeCycleScope
/**
 *[CoroutineScope] tied to this[LifecycleOwner]'s[Lifecycle].
 *
 * This scope will be cancelled when the[Lifecycle] is destroyed.
 *
 * This scope is bound to
 * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate].
 */
  • CoroutineScope는 LifecycleOwner에 종속되어있다.
    → Lifecycle이 파괴될때 scope또한 사라진다.
class MyFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewLifecycleOwner.lifecycleScope.launch {
            val params = TextViewCompat.getTextMetricsParams(textView)
            val precomputedText = withContext(Dispatchers.Default) {
                PrecomputedTextCompat.create(longTextContent, params)
            }
            TextViewCompat.setPrecomputedText(textView, precomputedText)
        }
    }
}

Fragment 를 사용하다보면 onCreateView() 함수이 호출 될 때, view 를 생성하고

onViewCreated() 함수에서 이미 생성한 view를 사용하는 경우가 많습니다.

viewLifeCycleOwner 는 onCreateView() 에서 생성되는 view의 LifeCycle 를 뜻 합니다.

그래서 onCreateView() ~ onDestoryView()  라이프 사이클을 가집니다.

  • 다음은 제가 사용중인 코루틴을 생성하는 Util함수입니다.

fun Fragment.launchAndRepeatOnLifecycle(
    state: Lifecycle.State = Lifecycle.State.STARTED,
    action: suspend CoroutineScope.() -> Unit
) {
		lifecycleScope.launch{
				viewLifecycleOwner.repeatOnLifecycle(state){
				action()
				}
    }
}

위 함수에는 몇가지 문제점이 있습니다.
state를 파라미터로 받고있는데 이를 관장하는 scope는 lifecycleScope입니다.

/**
*<li>{@linkLifecycle.Event#ON_CREATE created} after {@link#onViewStateRestored(Bundle)}</li>
*<li>{@linkLifecycle.Event#ON_START started} after {@link#onStart()}</li>
*<li>{@linkLifecycle.Event#ON_RESUME resumed} after {@link#onResume()}</li>
*<li>{@linkLifecycle.Event#ON_PAUSE paused} before {@link#onPause()}</li>
*<li>{@linkLifecycle.Event#ON_STOP stopped} before {@link#onStop()}</li>
*<li>{@linkLifecycle.Event#ON_DESTROY destroyed} before {@link#onDestroyView()}</li>
*</ol>

lifecycleScope는 onViewStateRestored다음으로 불리게 됩니다.
따라서 Lifecycle.State.CREATED로 설정하여도

onAttach - onCreateView - onViewCreated - onViewStateRestored - onStart - …순으로 불리게 되며 onViewCreated보다 늦게 불리게 됩니다.

즉 만약에 내가 onViewCreated에서 데이터를 세팅할때 모든 flow가 emit된 후에 Flow를 collect한다면 아무런 데이터를 받을수 없는 상황이 생깁니다.
(StateFlow나 SharedFlow(replay=1)을 이용하면 현재의 값을 방출해주므로 해결되지만 side effect가 발생할 가능성이 있다.)

따라서 우리는 이걸 변형해서 사용할 필요가 있다.


fun Fragment.launchAndRepeatOnLifecycle(
    state: Lifecycle.State = Lifecycle.State.STARTED,
    action: suspend CoroutineScope.() -> Unit
) {
		viewLifecycleOwner.lifecycleScope.launch{
				viewLifecycleOwner.repeatOnLifecycle(state){
				action()
				}
    }
}

기존의 fragment의 lifecycle을 보는것이 아니라 viewLifecycle을 보게 만들었다.

viewLifeCycleOwner는

💡 * {@link#getViewLifecycleOwner() lifecycle of the Fragment's View}. *

* This will be set to the new {@linkLifecycleOwner} after {@link#onCreateView} returns a * non-null View and will set to null after {@link#onDestroyView()}.

onCreateView이후에 생성되며 onDestroyView까지 유지가 된다.

따라서 위의 Util함수를 사용할때 onCreate에서 생성해서 onDestroy까지 유지하길 원해서
Lifecycle.State.CREATED로 넣는다면
viewLifecycleOwner의 생명주기를 따르게끔 만들어야 할 것이다.

profile
러닝커브를 따라서 등반중입니다.

0개의 댓글