마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

compose 옵저빙 어떻게 해야되나요?

0 추천
기존 코드

viewmodel.data.,observe(this, Observer {

 // 처리 코드

})

를 컴포즈로 하려고 하는데요 찾아보니

val test : DataModel by observeasstate 이런식으로 옵저빙을 해서 쓴다고 해서 쓰긴했는데

잘 안되더라구요

test에 관련된 데이터만 처리할게 아니라

viewmodel.test.observer {

// 여기에 test와 관련된 처리가 아닌 test에 변경이 될때마다 다른 ui도 변경 또는 갱신

}

이런 처리를 하려고 하는거거든요 remember도 생각해봤지만 이게 바뀔때마다 옵저빙이 알아서 타는것도 아니고

어떻게 해야되는건지 잘 모르겠네요 구글링해보면 저 관련 데이터만 처리하는것만 나오고

실제로 옵저빙했을때 다른 ui 도 실시간으로 계속 처리하는 방식을 못찾겠습니다 어떻게 해야되는건가요?
수원통학러 (3,570 포인트) 님이 2022년 7월 13일 질문

1개의 답변

0 추천

Compose는 기본적으로 수동적으로 데이터의 상태를 받아서 상태변경이 감지되면 compose function을 다시호출하여 변경된 부분만 업데이트하는(recompose) 방식으로 동작하도록 설계가 되어 있습니다. 따라서 변경된 상태 데이터를 compose function에 파라미터로 전달해서 compose function이 다시 호출되도록 처리해주어야 합니다.

ViewModel을 사용한다면, 모든 상태의 원본은 ViewModel에서 관리를 하고 compose function에서는 compose state를 통해서 ViewModel 의 상태데이터의 변경을 통보받도록 구성하여야 합니다.

@Composable
private fun MyScreenContent(
    uiState: MyUiState,
    onToggleClicked: (Boolean) -> Unit
) {
    Column {
        if (uiState.error != null) {
            ErrorMessageCard(message = uiState.error.message ?: "Unknown error")
        }
        
        ToggleButton(uiState.isToggleOn) { toggleValue ->
            onToggleClicked(toggleValue)
        }
    }
}

@Composable
fun ErrorMessageCard(
    message: String,
    contentPadding: Dp = 8.dp,
    icon: ImageVector = Icons.Default.Error,
    color: Color = MaterialTheme.colors.error
) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
    ) {
        Row(
            modifier = Modifier.padding(contentPadding),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Image(
                imageVector = icon,
                contentDescription = null,
                colorFilter = ColorFilter.tint(color)
            )
            Text(
                modifier = Modifier.padding(8.dp),
                text = message,
                color = color
            )
        }
    }
}


@Composable
private fun ToggleButton(
    isOn: Boolean,
    onClicked: (Boolean) -> Unit
) {
    Button(onClick = { onClicked(!isOn) }) {
        Text(text = if (isOn) "Off" else "On")
    }
}


class MyViewModel (private val repository: MyRepository): ViewModel() {

    var uiState by mutableStateOf(MyUiState())
        private set

    fun onToggleClicked(toggleValue: Boolean) {
        viewModelScope.launch {
            repository.updateToggle(toggleValue)
                .onFailure { th ->
                    uiState = uiState.copy(error = th)
                }
                .onSuccess {
                    uiState = uiState.copy(isToggleOn = true)
                }
        }
    }
}

data class MyUiState(
    val error: Throwable? = null, 
    val isToggleOn: Boolean = false
)

class MyRepository {
    suspend fun updateToggle(value: Boolean): Result<Boolean> = withContext(Dispatchers.IO) {
        Result.success(true)
    }
}



이걸 state hoisting이라고 하는데, compose에서 아주 핵심이 되는 부분이므로 잘 이해해 두어야 부작용이 없게 됩니다.

아래 개발자 문서를 꼼꼼히 읽어보세요.

https://developer.android.com/jetpack/compose/state

Edit: 버튼을 눌러서 토글 상태를 변경하는 간단한 샘플로 예제를 변경했습니다.

spark (224,800 포인트) 님이 2022년 7월 13일 답변
spark님이 2022년 7월 13일 수정
해당 문서를 봤었는데도 잘이해가 안되더라구요
observeasstate를 받아서 파라미터로 전달을 해야된다고 하셨는데
@composible
fun AUiUpdate() {
  // a ui 관련 코드
 observeasstate 받아옴
}
이런 코드가 있다면 여기서 다시 a라는 ui를 옵저빙해서 갱신시키고 싶으면
fun A_1UiUpdate()라는 composible함수를 또 만들어서 여기에 observeasstate를 넘겨주고 이 a_1uiupdate라는 메소드에 또 a 관련 ui를 써야되는건가요?
그러면 또 다시 옵저빙해서 갱신해야된다하면 a_2Ui 펑션을 만들어서 또넘겨주고 이런식 으로가야하는건지 궁금합니다
예를 들어 체크 on/off에 따른 ui를 갱신하고 싶은경우, 사용자가 여러번 on/off할텐데 체크가 여러개인경우 및 이에 따른 계산을 해야하는 경우 옵저빙을 실시간으로 하면서
ui를 갱신해줘야되는데 이런경우 어떻게 해야하는건지 감도 안잡힙니다
위 답글에 문서에 있는 예제코드를 살짝 변경해서 올려놨습니다.  Screen level compose function에서만 ViewModel의 상태 데이터를 observe하시고 Screen 내부에서는 UI에 따라 compose function 을 각각 만들고 여기에 ViewModel에 받아온 상태값을 파라미터로 넘기시면 됩니다.
on/off 부분도 마찬가지로 최상위 compose function 을 통해서 viewModel과 통신할 수 있도록 람다함수를 compose function에 파라미터로 넘겨서 처리합니다. Compose에서는 state는 컴포넌트에 내려주고, 이벤트를 컴포넌트로 받아서 최상위 compose function이 처리하는 것이 일반적입니다.
state나 처리와 user interaction 모두 Screen레벨의 최상위 compose function이 ViewModel을 호출할 수 있도록 구조를 가져가시면 됩니다.

Compose가 상태관리나 네비게이션처리 등과 관련해서 상당히 복잡한 프레임워크입니다. 따라서 개발자 문서와 구글 IO등 관련 자료를 일단 많이 접하시기를 강력히 권장합니다.
버튼을 눌러서 토글 상태를 변경하는 간단한 샘플로 예제를 변경했습니다.
...