Notice
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 스택 자바 코드
- rxjava disposable
- 안드로이드 os 구조
- 안드로이드 라이선스 종류
- 2022 플러터 안드로이드 스튜디오
- 안드로이드 유닛테스트란
- 안드로이드 레트로핏 사용법
- jvm 작동 원리
- 안드로이드 유닛 테스트 예시
- 클래스
- 큐 자바 코드
- 자바 다형성
- 안드로이드 라이선스
- 멤버변수
- 2022 플러터 설치
- 스택 큐 차이
- 안드로이드 유닛 테스트
- 서비스 쓰레드 차이
- ANR이란
- 플러터 설치 2022
- ar vr 차이
- jvm이란
- 객체
- 서비스 vs 쓰레드
- android ar 개발
- 안드로이드 레트로핏 crud
- rxjava hot observable
- rxjava cold observable
- Rxjava Observable
- android retrofit login
Archives
- Today
- Total
나만을 위한 블로그
[Android Compose] observeAsState란? 본문
728x90
반응형
틈틈이 Compose 공부용으로 뉴스 api를 사용한 뉴스 조회 앱을 만들고 있는데, 뷰모델에 함수를 생성한 후에 액티비티에서 호출하면 ApiResult.Success가 호출되지 않는 현상이 발생했다.
아래는 오류가 발생하던 메인 액티비티의 구현으로, 아직 LazyColumn을 사용하기 전의 코드다.
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val newsViewModel: NewsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeNewsAppTheme {
newsViewModel.getKoreaNewsHeadline()
newsViewModel.koreaNews.observe(this) {
when (it) {
is ApiResult.Loading -> {
// 프로그레스 바
Timber.e("## 뉴스 데이터 불러오는 중...")
}
is ApiResult.Success -> {
// 화면에 데이터 표시
Timber.e("## 뉴스 데이터 불러오기 성공 : ${it.data}")
it.data?.articles?.forEach { newsData ->
// 뉴스 데이터 각각 접근
}
}
is ApiResult.Error -> {
// 에러 토스트 표시
Timber.e("## 뉴스 데이터 불러오기 실패 : ${it.message}")
}
}
}
}
}
}
}
그러나 이 방식은 XML을 사용하는 전통적인 방식에서 작동하는 것이고, Compose에선 이렇게 사용하면 안 된다.
수정 포인트는 2곳이다.
- LaunchedEffect에서 뷰모델의 함수 호출
- observeAsState()를 사용해서 LiveData를 관찰하고, 변경될 때마다 Composable 함수를 자동 업데이트하게 변경
LaunchedEffect는 컴포저블 함수 안에서 suspend 함수를 호출하기 위한 장치라서 반드시 사용해야 하지만, observeAsState 함수는 처음 보는 함수다. 이 함수는 뭐하는 함수인가?
- LiveData.observeAsState() : LiveData를 관찰하기 시작하고 State를 통해 값을 나타낸다
해당 LiveData를 관찰하기 시작하고 State를 통해 값을 나타낸다. LiveData에 새 값이 게시될 때마다 리턴된 State가 업데이트되어 모든 State.value 사용이 재구성(recomposition)된다. 초기값은 이 LiveData가 아직 초기화되지 않았을 때만 사용된다. T가 null이 아닌 타입인 경우, 이 LiveData에 설정한 모든 값도 null이 아닌지 확인하는 것은 사용자의 책임이다. 내부 옵저버는 이 컴포저블이 폐기(disposes)되거나 현재 LifecycleOwner가 Lifecycle.State.DESTROYED 상태로 이동하면 자동 제거된다
정리하면 Compose에서 LiveData를 observe해서 값이 바뀌면 자동 갱신하기 위해 써야 하는 요소다.
위 2가지 요소를 적용하고 LazyColumn까지 사용해서 변경된 메인 액티비티의 코드는 아래와 같다.
class MainActivity : ComponentActivity() {
private val newsViewModel: NewsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeNewsAppTheme {
LaunchedEffect(Unit) {
newsViewModel.getKoreaNewsHeadline()
}
val newsData by newsViewModel.koreaNews.observeAsState()
newsData?.let { result ->
when (result) {
is ApiResult.Loading -> {
// 프로그레스 바 표시
Timber.e("## 뉴스 데이터 불러오는 중...")
}
is ApiResult.Success -> {
// 화면에 데이터 표시
Timber.e("## 뉴스 데이터 불러오기 성공 : ${result.data}")
LazyColumn(
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
items(result.data?.articles ?: listOf()) { newsItem: NewsData ->
NewsItem(
title = newsItem.title,
description = newsItem.description,
url = newsItem.url,
imageUrl = newsItem.urlToImage,
onUrlClick = { url ->
openWebViewWith(url)
}
)
}
}
}
is ApiResult.Error -> {
// 에러 토스트 표시
Timber.e("## 뉴스 데이터 불러오기 실패 : ${result.message}")
}
}
}
}
}
}
private fun openWebViewWith(url: String) =
Intent(Intent.ACTION_VIEW, Uri.parse(url)).also {
startActivity(it)
}
}
뷰모델의 구현은 특별할 게 없는 흔한 형태를 하고 있다.
@HiltViewModel
class NewsViewModel @Inject constructor(
private val newsRepository: NewsRepository
): ViewModel() {
private val _koreaNews = MutableLiveData<ApiResult<BaseNewsData<NewsData>>?>()
val koreaNews: LiveData<ApiResult<BaseNewsData<NewsData>>?> = _koreaNews
fun getKoreaNewsHeadline() = viewModelScope.launch {
_koreaNews.value = ApiResult.Loading()
val result = newsRepository.getKoreaNewsHeadline()
_koreaNews.value = result
}
}
반응형
'Android > Compose' 카테고리의 다른 글
[Android Compose] BottomTabNavigation 구현하는 법 (0) | 2024.02.25 |
---|---|
[Android Compose] CircularProgressIndicator 에러 해결 (0) | 2024.02.24 |
[Android Compose] Material 3 버전의 BottomSheet 구현하기 (0) | 2023.08.24 |
[Android Compose] 스켈레톤 뷰(Shimmer Loading Effect) 구현하기 (0) | 2023.08.24 |
[Android Compose] Swipe Refresh 구현하기 (0) | 2023.08.20 |
Comments