# 2022/01/11

매일 수정하는 GNOSS LV5·2022년 1월 11일
0

1일1문서

목록 보기
16/17

https://hongbeomi.medium.com/번역-android-ui에서-flow를-수집하는-안전한-길-bd8449e67ec3

Kotlin Flow 안전하게 사용하기

여러분들의 Flow는 안전합니까?

안드로이드 앱에서는 LiveData를 사용하여 데이터를 관찰하고 데이터의 변화에 유연하게 대처했습니다.
LiveData는 Activity 혹은 Fragment의 LifeCycle을 알고있으며 생성되고 파괴됨을 같이 진행하였습니다.
하지만 Multi platform에 따라 Activity,Fragment와 dependency가 높은 livedata의 불편함이 계속해서 나왔고 코틀린에서는 flow라는 것을 만들었습니다.

flow는 일반적으로 UI레이어에서 수집되어 화면에 데이터를 업데이트 해줍니다.
하지만 flow를 수집하여 필요하지 않거나 전부 수집되기 전에 view가 죽게 된다면 리소스가 낭비될 수 있습니다.
이 글에서는 리소스를 낭비하지 않도록 보호하는 방법을 제시합니다.

flow의 가장 중요한 관점은 안전하게 수집하는 것입니다.
channel, buffer, flowOn와 같은 버퍼를 사용하는 flow는 CoroutineScope와 같은 API를 사용하여 수집하기에 안전하지 않습니다.
수집 도중 백그라운드로 돌아가게 된다면 수집중인 Job을 취소해주어야하는 문제점이 생깁니다.

즉 결론적으로 우리가 원하는 것은 LiveData처럼 View의 생명주기를 알면서 flow로 수집을 하는 도중 백그라운드로 돌아가게 된다면 수집중인 job을 취소시키고 리소스의 낭비 위험성을 줄이는 작업이 필요합니다.

참고 : 이 API는 lifecycle:lifecycle-runtime-ktx:2.4.0-alpha01 라이브러리에서(혹은 그 이상 버전) 사용할 수 있습니다.


Lifecycle.repeatOnLifecycle

activity의 예시

class LocationActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Create a new coroutine since repeatOnLifecycle is a suspend function
        lifecycleScope.launch {
            // The block passed to repeatOnLifecycle is executed when the lifecycle
            // is at least STARTED and is cancelled when the lifecycle is STOPPED.
            // It automatically restarts the block when the lifecycle is STARTED again.
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Safely collect from locationFlow when the lifecycle is STARTED
                // and stops collection when the lifecycle is STOPPED
                locationProvider.locationFlow().collect {
                    // New location! Update the map
                }
            }
        }
    }
}

repeatOnLifecycle은 매개변수로 전달된 [Lifecycle.State](https://developer.android.com/reference/android/arch/lifecycle/Lifecycle.State)가 해당 state에 도달하면 블록에 있는 새 코루틴을 자동으로 생성 및 시작하고, lifecycle이 state 아래로 떨어질 때 블록 안에 있는 실행 중인 코루틴을 취소하는 suspend 함수입니다.

이렇게 하면 코루틴이 더 이상 필요하지 않을 때 코루틴을 취소하는 코드가 repeatOnLifecycle에 의해 자동으로 실행되므로 보일러 플레이트 코드를 피할 수 있습니다.

fragement의 예시

class LocationFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // ...
        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                locationProvider.locationFlow().collect {
                    // New location! Update the map
                }
            }
        }
    }
}

중요한 점 : fragment는 항상 viewLifecycleOwner를 사용하여 UI 업데이트를 트리거해야합니다. 하지만 가끔 View가 없는 DialogFragment의 경우에는 그렇지 않습니다. DialogFragment의 경우 lifecycleOwner를 사용할 수 있습니다.

Flow.flowWithLifecycle

만약 수집할 flow가 하나만 있다면 내부 연산자를 사용할 수 있습니다.
이 API는 내부적으로 repeatOnLifecycle을 사용합니다.

class LocationActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Listen to one flow in a lifecycle-aware manner using flowWithLifecycle
        lifecycleScope.launch {
            locationProvider.locationFlow()
                .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
                .collect {
                    // New location! Update the map
                }
        }
        
        // Listen to multiple flows
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // As collect is a suspend function, if you want to collect
                // multiple flows in parallel, you need to do so in 
                // different coroutines
                launch {
                    flow1.collect { /* Do something */ }   
                }
                
                launch {
                    flow2.collect { /* Do something */ }
                }
            }
        }
    }
}
profile
러닝커브를 따라서 등반중입니다.

0개의 댓글