#7 [feat] 우리 집 규칙 기능 구현 - #8
Conversation
|
|
||
| private val viewModel: OurRulesViewModel by hiltNavGraphViewModels(R.id.nav_our_rules) | ||
| private var ourRulesAdapter: OurRulesAdapter? = null |
There was a problem hiding this comment.
hiltNavGraphViewModels 의도가 궁금합니다
There was a problem hiding this comment.
저희는 hilt를 사용하기 때문이지요 :D
private val viewModel: OurRulesViewModel by navGraphViewModels(R.id.nav_our_rules)위의 방식으로 생성된 모든 ViewModel 객체는 연결된 NavHost와 ViewModelStore가 삭제되거나 nav_graph가 백 스택에서 소멸되기 전까지 유지되기 때문에 by navGraphViewModels(layoutRes)을 사용하는 것입니다.
그런데 우리는 hilt를 사용하기 때문에 by hiltNavGraphViewModels(R.id.nav_our_rules)을 사용해줬습니당
If your ViewModel is scoped to the navigation graph, use the hiltNavGraphViewModels function that works with fragments that are annotated with @androidentrypoint.
관련 공식 문서: https://developer.android.com/training/dependency-injection/hilt-jetpack#viewmodel-navigation
| @AndroidEntryPoint | ||
| class OurRulesActivity : AppCompatActivity() { | ||
| override fun onCreate(savedInstanceState: Bundle?) { |
There was a problem hiding this comment.
one Activity 를 의도한 것인지 아닌지 궁금합니다.
one Activity 를 의도했음 이 OurRulesActivity 가 없어도 된다고 생각하고,
one Activity 를 의도하지 않았으면 OurRulesFragment 의 로직이 OurRulesActivity 로 옮겨져야 한다고 생각하는데
어떤 의도인지 궁금합니다
| private var _uiState = MutableStateFlow(OurRulesUiState()) | ||
| val uiState: StateFlow<OurRulesUiState> | ||
| get() = _uiState.asStateFlow() | ||
| private var _isEmptyRepresentativeRuleList = MutableStateFlow(true) | ||
| val isEmptyRepresentativeRuleList: StateFlow<Boolean> | ||
| get() = _isEmptyRepresentativeRuleList.asStateFlow() | ||
| private var _isEmptyGeneralRuleList = MutableStateFlow(true) | ||
| val isEmptyGeneralRuleList: StateFlow<Boolean> | ||
| get() = _isEmptyGeneralRuleList.asStateFlow() |
There was a problem hiding this comment.
| private var _uiState = MutableStateFlow(OurRulesUiState()) | |
| val uiState: StateFlow<OurRulesUiState> | |
| get() = _uiState.asStateFlow() | |
| private var _isEmptyRepresentativeRuleList = MutableStateFlow(true) | |
| val isEmptyRepresentativeRuleList: StateFlow<Boolean> | |
| get() = _isEmptyRepresentativeRuleList.asStateFlow() | |
| private var _isEmptyGeneralRuleList = MutableStateFlow(true) | |
| val isEmptyGeneralRuleList: StateFlow<Boolean> | |
| get() = _isEmptyGeneralRuleList.asStateFlow() | |
| private var _uiState = MutableStateFlow(OurRulesUiState()) | |
| val uiState: StateFlow<OurRulesUiState> = _uiState.asStateFlow() | |
| private var _isEmptyRepresentativeRuleList = MutableStateFlow(true) | |
| val isEmptyRepresentativeRuleList: StateFlow<Boolean> = _isEmptyRepresentativeRuleList.asStateFlow() | |
| private var _isEmptyGeneralRuleList = MutableStateFlow(true) | |
| val isEmptyGeneralRuleList: StateFlow<Boolean> = _isEmptyGeneralRuleList.asStateFlow() |
이렇게 해도 동작을 할텐데 get()을 붙인 이유가 있을까요??
There was a problem hiding this comment.
아 제가 맨날 LiveData만 사용하다보니.. get()이 습관이 들어서 히히.. 감사합니다 😄
| _uiState.value = _uiState.value.copy( | ||
| ourRuleList = tmpRuleList.toList() | ||
| ) |
There was a problem hiding this comment.
| _uiState.value = _uiState.value.copy( | |
| ourRuleList = tmpRuleList.toList() | |
| ) | |
| _uiState.value = _uiState.value.copy( | |
| ourRuleList = tmpRuleList | |
| ) |
toList() 는 없애도 될 것 같습니다
| // dummyData | ||
| // listOf( | ||
| // RuleInfo("1111", "우리 집 대장은 최코코"), | ||
| // RuleInfo("2222", "10시, 18시 코코님 밥 챙겨드리기"), | ||
| // RuleInfo("3333", "자기가 먹은 건 자기가 치우기!"), | ||
| // RuleInfo("4444", "아침에 일어나면 이불 정리하기"), | ||
| // RuleInfo("5555", "밤 10시 지나면 이어폰 끼기"), | ||
| // RuleInfo("11412311", "우리 집 대장은 최코코"), | ||
| // RuleInfo("4444", "아침에 일어나면 이불 정리하기"), | ||
| // RuleInfo("512355", "밤 10시 지나면 이어폰 끼기"), | ||
| // RuleInfo("612366", "냄새나는 음식 먹고나서 환기하기"), | ||
| // RuleInfo("7721377", "맛있는 식당 찾으면 공유하기"), | ||
| // RuleInfo("112311", "우리 집 대장은 최코코"), | ||
| // RuleInfo("2221322", "10시, 18시 코코님 밥 챙겨드리기"), | ||
| // RuleInfo("3331233", "자기가 먹은 건 자기가 치우기!"), | ||
| // RuleInfo("4412344", "아침에 일어나면 이불 정리하기"), | ||
| // RuleInfo("5552135", "밤 10시 지나면 이어폰 끼기"), | ||
| // RuleInfo("6612366", "냄새나는 음식 먹고나서 환기하기"), | ||
| // RuleInfo("7771237", "맛있는 식당 찾으면 공유하기") | ||
| // ) |
| inline fun LifecycleOwner.repeatOnStarted(crossinline block: suspend () -> Unit) { | ||
| when (this) { | ||
| is AppCompatActivity -> { | ||
| lifecycleScope.launch { | ||
| lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { | ||
| block() | ||
| } | ||
| } | ||
| } | ||
| is Fragment -> { | ||
| lifecycleScope.launch { | ||
| viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { | ||
| block() | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
reapeatOnLifeCycle 함수 자체가 Lifecycle 이 onStart 상태일 때 수집을 시작하고, onResume 상태가 되면 알아서 수집을 멈춰주는 함수인 것으로 압니다.
이 확장함수를 만든 의도가 궁금합니다
There was a problem hiding this comment.
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
}
}
}
}
}reapeatOnLifeCycle함수는 onResume 상태가 아닌 onStop상태일 때 collect가 cancel됩니다!!
사실, collect를 한 번만 하게 된다면 Flow.flowWithLifecycle를 사용해도 되긴합니다만..
추후에 기능이 추가된다면 collect를 또 할 수도 있을 것 같아서 저는 reapeatOnLifeCycle을 선호하는 편입니다!
관련 링크
또한, 이 뷰 뿐만 아니라 다른 뷰(Fragment/Activity)에서도 collect해줄 때마다 더 간결하게 사용하기 위해 확장함수를 만들었습니다!!
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
// do something~
}
}| <!-- Base application theme. --> | ||
| <style name="Theme.HousReleaseAos" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> | ||
| <style name="Theme.HousReleaseAos" parent="Theme.MaterialComponents.DayNight.NoActionBar"> | ||
| <!-- Primary brand color. --> |
| android:exported="true" > | ||
| <intent-filter> | ||
| <action android:name="android.intent.action.MAIN" /> | ||
|
|
||
| <category android:name="android.intent.category.LAUNCHER" /> | ||
| </intent-filter> | ||
| </activity> | ||
| <activity | ||
| android:name=".MainActivity" | ||
| android:exported="true"> | ||
| </activity> |
There was a problem hiding this comment.
PR 올릴 땐, MainActivity를 시작점으로 설정해주세요!
There was a problem hiding this comment.
PR 올릴 땐, MainActivity를 시작점으로 설정해주세요!
LoginActivity도 false로 올려야 할까요?
| repeatOnStarted { | ||
| viewModel.uiState.collect { uiState -> | ||
| ourRulesAdapter?.let { ourRulesAdapter -> | ||
| // this callback runs when the list is updated | ||
| ourRulesAdapter.submitList(uiState.ourRuleList) { | ||
| binding.rvOurRules.invalidateItemDecorations() | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
uiState만을 수집할 땐, reapeatLifeCycle 보단 flowWithLifeCycle 함수를 사용해주세요!
There was a problem hiding this comment.
#8 (comment)
혹시, 이유를 알 수 있을까요?! 하나만 수집할 때는 reapeatLifeCycle보다 flowWithLifeCycle가 더 성능이 좋나요?!
| data class RuleInfo( | ||
| val id: String = "", | ||
| val description: String = "" | ||
| ) |
There was a problem hiding this comment.
어제 제가 슬랙에 올린 글을 보면
의미 있게 구분하라 (불용어 -noise word- 를 쓰지 말자)
부분을 한번 읽어주세요!
이거 보니까 뒤 Info 사용을 지양하고 싶단 생각이 들었습니다.
그냥 제 생각이긴 한데 data 모듈에선 ruleDto domain 모듈에선 rule 사용은 어떨까요?
다음 안드 회의가 있을 때 다같이 논의해 봅시다
There was a problem hiding this comment.
아 맞다.. 이거 쓰면서도 찜찜했는데 굿입니다!!
|
@KWY0218 repeatOnLifecycle에 launchIn해도 잘 되긴하네요. repeatOnStarted {
viewModel.uiState.onEach { uiState ->
ourRulesAdapter?.let { ourRulesAdapter ->
// this callback runs when the list is updated
ourRulesAdapter.submitList(uiState.ourRuleList) {
binding.rvOurRules.invalidateItemDecorations()
}
}
}.launchIn(lifecycleScope)
}
@OptIn(ExperimentalCoroutinesApi::class)
public fun <T> Flow<T>.flowWithLifecycle(
lifecycle: Lifecycle,
minActiveState: Lifecycle.State = Lifecycle.State.STARTED
): Flow<T> = callbackFlow {
lifecycle.repeatOnLifecycle(minActiveState) {
this@flowWithLifecycle.collect {
send(it)
}
}
close()
}이 부분에 대해서 다음주 심화스터디 flow 발표할 때 자세히 파보고 정리해볼게요~~ |


관련 이슈
작업 뷰
작업한 내용
PR 포인트
safeLet 확장함수(사용 방법은 아래 링크 참조해주세요 ~)
TeamHous/Hous-Android#179 (comment)
repeatOnStarted Lifecycle확장함수
build gradle 추가
Unsupported metadata version when using Hilt 2.42 with Kotlin 1.7.10 google/dagger#3470