제 마음대로 보고 싶은 부분을 우선순위로 해서 분석하였습니다.
공식 가이드는 아래의 링크를 보고 따라하면되겠습니다.
https://github.com/android/nowinandroid/blob/main/docs/ArchitectureLearningJourney.md
1. 메인 화면
For you / Saved / Interests 메뉴는
NiaNavHost.kt에 NavHost 에서 NavGraphBuilder로 구현되어 있다.
NiaNavHost.kt
@Composable
fun NiaNavHost(
navController: NavHostController,
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
startDestination: String = forYouNavigationRoute
) {
NavHost(
navController = navController,
startDestination = startDestination,
modifier = modifier,
) {
forYouScreen()
bookmarksScreen()
interestsGraph(
navigateToTopic = { topicId ->
navController.navigateToTopic(topicId)
},
navigateToAuthor = { authorId ->
navController.navigateToAuthor(authorId)
},
nestedGraphs = {
topicScreen(onBackClick)
authorScreen(onBackClick)
}
)
}
}
2. "For you" navigation graph 분석
fun NavGraphBuilder.forYouScreen() {
composable(route = forYouNavigationRoute) {
ForYouRoute()
}
}
ForYouScreen Composable은 아래의 submodule로 구성되어있다.
app의 gradle에 해당 project(foryou)가 implementation 되어있으므로 사용가능
ForYouScreen.kt 은 아래와 같이 프리뷰를 제공한다
또한 ViewModel UnitTest와 androidTest를 제공하여 이를 보면 좋을 것같다.
3. ForYouScreen.kt
@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
internal fun ForYouRoute(
modifier: Modifier = Modifier,
viewModel: ForYouViewModel = hiltViewModel()
) {
val interestsSelectionState by viewModel.interestsSelectionUiState.collectAsStateWithLifecycle()
val feedState by viewModel.feedState.collectAsStateWithLifecycle()
val isSyncing by viewModel.isSyncing.collectAsStateWithLifecycle()
ForYouScreen(
isSyncing = isSyncing,
interestsSelectionState = interestsSelectionState,
feedState = feedState,
onTopicCheckedChanged = viewModel::updateTopicSelection,
onAuthorCheckedChanged = viewModel::updateAuthorSelection,
saveFollowedTopics = viewModel::saveFollowedInterests,
onNewsResourcesCheckedChanged = viewModel::updateNewsResourceSaved,
modifier = modifier
)
}
viewModel은 collectAsStateWithLifecycle을 통해 state 변화를 ViewModel에서 감지하고 이를 ForYouScreen에 binding 시키는 것으로 보인다.
알아보아야할 것 : collectAsStateWithLifecycle(https://witcheryoon.tistory.com/338)
3.1 ViewModel 부분
먼저,
HiltViewModel을 인스턴스하며, 이는 외부 argument로 넣고 있지않으므로, DI 부분을 확인해야할 것이다.
@OptIn(SavedStateHandleSaveableApi::class)
@HiltViewModel
class ForYouViewModel @Inject constructor(
syncStatusMonitor: SyncStatusMonitor,
private val userDataRepository: UserDataRepository,
private val getSaveableNewsResourcesStream: GetSaveableNewsResourcesStreamUseCase,
getSortedFollowableAuthorsStream: GetSortedFollowableAuthorsStreamUseCase,
getFollowableTopicsStream: GetFollowableTopicsStreamUseCase,
savedStateHandle: SavedStateHandle
) : ViewModel() {
...
}
ViewModel의 인자로 UserRepository와 UserCase를 Case별로 나누어 인자로 받아 들이고 있으며, SavedStateHandle로 state View 를 Handle한다 메소드는 아래를 참고하라.
알아보아야할 것 : SavedStateHandle(https://witcheryoon.tistory.com/337)
Viewmodel은 StateFlow를 리턴하며, 그 중간에 UseCase들이 동작하여 UseCase는 invoke가 구현되어 이것이 각 부분에 필요한 Repository의 정보를 flow로 리터한다. Usecase는 대부분 예를 들어 아래와 같이 구성되어있다.
class GetSortedFollowableAuthorsStreamUseCase @Inject constructor(
private val authorsRepository: AuthorsRepository
) {
/**
* Returns a list of authors with their associated followed state sorted alphabetically by name.
*
* @param followedTopicIds - the set of topic ids which are currently being followed.
*/
operator fun invoke(followedAuthorIds: Set<String>): Flow<List<FollowableAuthor>> {
return authorsRepository.getAuthorsStream().map { authors ->
authors
.map { author ->
FollowableAuthor(
author = author,
isFollowed = author.id in followedAuthorIds
)
}
.sortedBy { it.author.name }
}
}
}
3.2 ForYouScreen
@Composable
internal fun ForYouScreen(
isSyncing: Boolean,
interestsSelectionState: ForYouInterestsSelectionUiState,
feedState: NewsFeedUiState,
onTopicCheckedChanged: (String, Boolean) -> Unit,
onAuthorCheckedChanged: (String, Boolean) -> Unit,
saveFollowedTopics: () -> Unit,
onNewsResourcesCheckedChanged: (String, Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
...
}
이 컴포저블은 크게
LazyVerticalGrid 부분과 애니메이션 효과를 나타내는 AnimatedVisibility로 구성되어 있다
여기서 interestSelction과 newsFeed는 LazyGridScope의 extension function이며, 이둘을 Vertical Grid로 연결한 모습임.
추가된 부분 : 2022-11-14 ) project 전체 구조 중 재구성해 볼 부분을 아래와 같이 요약하였음
참고 :
jankstats:
https://developer.android.com/topic/performance/jankstats
SavedStateHandle :
https://witcheryoon.tistory.com/337
https://pluu.github.io/blog/android/2020/02/20/savedstatehandle/
collectAsStateWithLifecycle :
https://witcheryoon.tistory.com/338
'Android Dev > NowInAndroid' 카테고리의 다른 글
NowInAndroid(4) - SyncWorker/SyncUtilities (0) | 2022.11.03 |
---|---|
NowInAndroid(3) - (Hilt) Viewmodel + DI overall flow, 재구성 예제 (0) | 2022.11.01 |
NowInAndroid(2) - ForYouViewModel (0) | 2022.10.28 |
collectAsStateWithLifecycle 요약 (0) | 2022.10.28 |
SavedStateHandle 사용 (0) | 2022.10.25 |