How are Android activities handled with Jetpack Compose and Compose Navigation?
기존에 Jetpack Navigation라이브러리를 사용하여 SAA로 개발을 진행하였다. 그렇다면 Compose를 사용할 때는 어떻게 진행하면 될까?
Compose
앱은 SAA를 사용하도록 디자인되어 있다.
여러개의 액티비나 프래그먼트를 setcontent
를 사용하여 각각 추가할 수 있지만, 액티비티사이의 데이터 전달에 대해 유의해야 한다. 이러한 방식은 기존의 앱에 새로운 컴포즈 화면을 추가하는 등 오래된 방식이다.
컴포즈에서는 하나의 액티비티내에서 Compose Navigtaion
을 사용하여 더 쉽고, 더 적은 코드로, 데이터 전송에 있어 더 나은 퍼포먼스를 제공한다.
그전에 Side-Effect에 대해 알고가야 할 것 같다.
Side-Effect
는 컴포저블 외부에서 발생하는 앱 상태의 변화를 말한다.
자신이 아닌 외부의 상태에 영향을 받는 것을 의미
이러한 외부효과를 관리하기위해 여러 함수들이 있는데
@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun LaunchedEffect(
key1: Any?,
key2: Any?,
key3: Any?,
block: suspend CoroutineScope.() -> Unit
) {
val applyContext = currentComposer.applyCoroutineContext
remember(key1, key2, key3) { LaunchedEffectImpl(applyContext, block) }
}
LaunchedEffect
함수는 현재 화면에 컴포지션이 일어날 때 필요한 작업을 실행하는 데 사용한다.
또한 이는 현재 컴포저블에 바인딩된 코루틴 컨텍스트
에서 실행된다. 즉, suspend
함수를 실행할 수 있으며 뷰 계층에서 뷰가 사라지면(컴포저블이 사라지면) 코루틴이 취소된다.
해당 key1,key2값을 기준값으로 두어 key가 바뀔 때만 suspend fun을 취소하고 재실행할 수 있다.
@Composable
fun MyScreen(
state: UiState<List<Movie>>,
scaffoldState: ScaffoldState = rememberScaffoldState()
) {
if (state.hasError) {
// `scaffoldState.snackbarHostState`가 바뀌면
// LaunchedEffect는 취소되고 재시작 된다.
LaunchedEffect(scaffoldState.snackbarHostState) {
// 스낵바를 보여주는 와중 코루틴이 캔슬되면 자동으로 사라진다.
// state.hasError가 false라면 코루틴은 종료된다.
//
scaffoldState.snackbarHostState.showSnackbar(
message = "Error message",
actionLabel = "Retry message"
)
}
}
Scaffold(scaffoldState = scaffoldState) {
/* ... */
}
}
현재 Composable의 코루틴스콥을 참조하여 외부에서 실행할 수 있도록 한다.
컴포저블 외부에 있지만 컴포지션을 종료한 후 자동으로 취소되도록 범위가 지정된 코루틴을 실행하고 싶을 때 사용
Composable이 파괴될 때 같이 파괴된다.
@Composable
fun MoviesScreen(scaffoldState: ScaffoldState = rememberScaffoldState()) {
// MoviesScreen의 라이프사이클을 가진 코루틴 스코프
val scope = rememberCoroutineScope()
Scaffold(scaffoldState = scaffoldState) {
Column {
Button(
onClick = {
// Create a new coroutine in the event handler to show a snackbar
scope.launch {
scaffoldState.snackbarHostState.showSnackbar("Something happened!")
}
}
) {
Text("Press me")
}
}
}
}
리컴포지션이 재실행 되지 않아도 되는 State를 정의한다.
키가 변경되거나 컴포저블을 종료한 후 정리해야 하는 부수적인 작업을 수행한다.
Using rememberCoroutineScope() vs LaunchedEffect
LaunchedEffect
는 컴포저블이 처음 컴포지션되거나 리컴포지션이 될 때, key 값이 변경될 때 수행할 작업을 수행한다. 예를들어 ViewModel로 부터 데이터요청하기, 초기 애니메이션 실행하기 등등..
rememberCoroutineScope
는 코루틴 스콥을 저장하여 특정 코드에서 suspend함수를 실행할 수 있게 해준다.
rememberCoroutineScope
는 컴포지션이 일어나도 코루틴 스코프에 대한 참조를 유지한다.(LaunchedEffect
는 유지하지 않고 Cancel되고 다시 실행됨) 주어진 Composable이 완전히 삭제됬을 때만 코루틴이 멈춘다.
예를들어 A -> B -> C 순서대로 컴포저블을 생성한다고 했을 때, C에서 rememberCoroutineScope
을 실행한다면 C가 삭제됬을 때 코루틴은 자동으로 캔슬된다. 하지만 A에서 rememberCoroutineScrope
을 실행한다면 C가 삭제되어도 코루틴은 계속 돌아간다.(A가 살아있기 때문에)