collectAsState() vs collectAsStateWithLifecycle

KEH·3일 전
0

Compose

목록 보기
1/1
post-thumbnail

Jetpack Compose 의 collectAsState()collectAsStateWithLifecycle() 두 API 에 대해 알아봅시다.

두 API 는 모두 flow 데이터를 수집하여 Compose UI state 로 변환할 때 사용하는 composable 함수입니다.
그렇다면 두 API 는 어떤 차이점이 있고, 어떤 상황에서 사용되어야 할까요?

collectAsState()

collectAsState()Composition 의 라이프사이클을 따라갑니다.
출처 https://medium.com/androiddevelopers/consuming-flows-safely-in-jetpack-compose-cde014d0d5a3

Enter the Composition(최초 Composable 함수 호출) 단계에서 flow 데이터가 수집되기 시작하고, Leave the Composition(Composable 함수 제거) 단계에서 flow 데이터 수집을 중단합니다.

collectAsStateWithLifecycle()

collectAsStateWithLifecycle() 은 안드로이드 앱의 라이프사이클을 따라갑니다.
따라서 Lifecycle.State.STARTED 상태일 때 데이터를 수집하기 시작하고, Lifecycle.State.STARTED 상태가 아닐 때 데이터 수집을 중단합니다.

Example

예제 코드를 통해 collectAsState()collectAsStateWithLifecycle() 의 차이를 알아봅시다.

class MainViewModel: ViewModel() {
    private val _cnt: MutableStateFlow<Int> = MutableStateFlow(0)
    val cnt: StateFlow<Int> get() = _cnt.asStateFlow()

    init {
        test()
    }

    private fun test() {
        viewModelScope.launch {
            for (i in 1 .. 20) {
                delay(1000L)
                _cnt.value = i
            }
        }
    }
}

MainViewModel.kt 클래스에 cnt 라는 StateFlow 변수가 존재합니다.
test() 함수에서 1초마다 cnt 변수를 1씩 증가합니다.

class MainActivity : ComponentActivity() {
    private val mainViewModel: MainViewModel = MainViewModel()

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

        enableEdgeToEdge()

        setContent {
            val test1 by mainViewModel.cnt.collectAsState()
            val test2 by mainViewModel.cnt.collectAsStateWithLifecycle()

            WeatherTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Column(
                        modifier = Modifier.padding(innerPadding)
                    ) {
                        Greeting(name = "test1(collectAsState): $test1")

                        Greeting(name = "test2(collectAsStateWithLifecycle): $test2")
                    }
                }
            }
        }
    }

    override fun onResume() {
        super.onResume()

        Log.d("[TEST] KEH", "=========================== onResume ===========================")
    }

    override fun onStop() {
        super.onStop()

        Log.d("[TEST] KEH", "=========================== onStop ===========================")
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Log.d("[TEST] KEH", "name: $name")

    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

MainActivity.kt 에서 test1 변수와 test2 변수는 MainViewModelcnt 변수를 수집합니다.
단, test1collectAsState() 방식으로, test2collectAsStateWithLifecycle() 방식으로 수집합니다.

그리고 test1test2Greeting() 컴포저블 함수를 통해 텍스트로 화면에 표출됩니다.

onResumeonStop 에서 로깅을 한 이유는 onStartonPause 시점보다 두 방식의 차이를 더 명확하게 확인할 수 있기 때문입니다.

로깅 결과를 확인해보면 아래와 같습니다.

=========================== onResume ===========================
test1(collectAsState): 0
test2(collectAsStateWithLifecycle): 0
test1(collectAsState): 1
test2(collectAsStateWithLifecycle): 1
test1(collectAsState): 2
test2(collectAsStateWithLifecycle): 2
test1(collectAsState): 3
test2(collectAsStateWithLifecycle): 3
test1(collectAsState): 4
test2(collectAsStateWithLifecycle): 4
test1(collectAsState): 5
test2(collectAsStateWithLifecycle): 5
test1(collectAsState): 6
test2(collectAsStateWithLifecycle): 6
test1(collectAsState): 7
test2(collectAsStateWithLifecycle): 7
test1(collectAsState): 8
test2(collectAsStateWithLifecycle): 8
=========================== onStop ===========================
test1(collectAsState): 9
test1(collectAsState): 10
test1(collectAsState): 11
test1(collectAsState): 12
test1(collectAsState): 13
test1(collectAsState): 14
test1(collectAsState): 15
=========================== onResume ===========================
test2(collectAsStateWithLifecycle): 15
test1(collectAsState): 16
test2(collectAsStateWithLifecycle): 16
test1(collectAsState): 17
test2(collectAsStateWithLifecycle): 17
test1(collectAsState): 18
test2(collectAsStateWithLifecycle): 18
test1(collectAsState): 19
test2(collectAsStateWithLifecycle): 19
test1(collectAsState): 20
test2(collectAsStateWithLifecycle): 20

처음 onResume 상태에서는 test1test2 모두 정상적으로 데이터를 수집합니다.
onPause 상태가 되자 앱 라이프사이클을 인식하는 test2 는 데이터 수집을 중단하고, test1 만 데이터를 수집합니다.
다시 onResume 상태로 돌아오면 test2 는 데이터 수집을 재시작하여 test1, test2 변수 데이터를 수집하는 것을 확인할 수 있습니다.

결론

출처 https://medium.com/androiddevelopers/consuming-flows-safely-in-jetpack-compose-cde014d0d5a3
1. collectAsState() 방식은 Composition 라이프사이클을 따라 데이터 수집을 시작 및 중단합니다.
2. collectAsStateWithLifecycle() 방식은 앱 라이픗사이클을 따라 데이터 수집을 시작 및 중단합니다.
3. 안드로이드 앱에서 라이프사이클에 따라 데이터 상태 관리는 굉장히 중요합니다. 만약 collectAsState() 방식을 사용하여 데이터를 수집한다면 백그라운드 상태에서도 계속 데이터가 수집될 것이고, 보이고 있지 않은 UI 를 계속 리컴포지션하게 됩니다. 이는 휴대폰의 배터리 소모량을 늘리는 원인이 됩니다.
4. 따라서 안드로이드 앱을 개발할 때에는 collectAsStateWithLifecycle() 방식이 권장됩니다.
5. 안드로이드 앱 이외에 다른 플랫폼을 개발할 때에는 앱의 라이프사이클을 고려할 필요가 없으므로 collectAsState() 방식이 사용됩니다.



궁금한 점이나 잘못된 내용이 있을 경우 댓글 부탁드립니다.
읽어주셔서 감사합니다 :)



출처 - Consuming flows safely in Jetpack Compose

profile
개발을 즐기고 잘하고 싶은 안드로이드 개발자입니다 :P

0개의 댓글