[Android / Kotlin] Room 응답을 Flow로 변환하기

Subeen·2024년 2월 18일
0

Android

목록 보기
62/73

Room의 LiveData 응답을 Flow로 변경하는 과정을 정리해보고자 한다. 코드를 리팩토링하며 어느정도 감은 잡히는 것 같은데... Flow의 개념에 대해 다시 공부하는 시간을 가져야 할 것 같다 😵‍💫

Room의 Query 응답을 LiveData에서 Flow로 교체

@Dao
interface VideoDAO {
    @Query("SELECT * FROM videos")
    fun getVideos(): LiveData<List<VideoBasicModel>> // 기존 코드 
import kotlinx.coroutines.flow.Flow
@Dao
interface VideoDAO {
    @Query("SELECT * FROM videos")
    fun getVideos(): Flow<List<VideoBasicModel>> // 수정 된 코드

Repository의 반환 타입 변경

  • YoutubeRepository
interface YoutubeRepository {
    suspend fun getSearchingVideos(
        part: String = PART_SNIPPET,
        searchText: String,
        maxResults: Int = Constants.API_MAX_RESULT,
        regionCode: String = Constants.API_REGION,
        apiKey: String = Constants.API_KEY
    ): SearchModel
    
    fun getVideos(): Flow<List<VideoBasicModel>>
  • YoutubeRepositoryImpl
class YoutubeRepositoryImpl(
    private val db: VideoSearchDatabase
) : YoutubeRepository {

    override suspend fun getSearchingVideos(
        part: String,
        searchText: String,
        maxResults: Int,
        regionCode: String,
        apiKey: String
    ): SearchModel {
        return RetrofitInstance.api.getSearchingVideos(searchText = searchText)
    }
    
    override fun getVideos(): Flow<List<VideoBasicModel>> = 
        db.videoDao().getVideos()
}

ViewModel의 반환 타입 변경

class SharedViewModel(
    private val youtubeRepository: YoutubeRepository
) : ViewModel() {

    val searchResults: Flow<List<VideoBasicModel>> get() = youtubeRepository.getVideos()
    ...
}

Query 결과를 UI에 표시

observe를 사용하던 LiveData와는 달리 Flow는 Coroutine 안에서 Collect나 CollectLatest를 써서 구독해야 한다.

    private fun initViewModel() {
//        sharedViewModel.searchResults.observe(viewLifecycleOwner) {
//            homeListAdapter.submitList(it)
//        }
        lifecycleScope.launch {
            sharedViewModel.searchResults.collectLatest {
                homeListAdapter.submitList(it)
            }
        }
    }

StateFlow로 변환

StateFlow로 변환하여 Flow 동작을 Fragment의 LifeCycle과 동기화 시켜준다.

class SharedViewModel(
    private val youtubeRepository: YoutubeRepository
) : ViewModel() {
	/*
     * stateIn을 사용하여 Flow 타입을 StateFlow로 변경해준다. 
     * scope는 viewModelScope
     * 구독을 하는 시점은 WhileSubscribed(5000)
     * list는 초기값을 넣어주면 된다. 
     */
    val searchResults: StateFlow<List<VideoBasicModel>> get() = youtubeRepository.getVideos()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), listOf())
	...
}

StateFlow 구독

    private fun initViewModel() {
//        lifecycleScope.launch {
//            sharedViewModel.searchResults.collectLatest {
//                homeListAdapter.submitList(it)
//            }
//        }

		// SateFlow의 구독이 되면서 Fragment의 LifeCycle과 연동이 된다. 
        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                sharedViewModel.searchResults.collectLatest {
                    homeListAdapter.submitList(it)
                }
            }
        }
    }

확장함수 만들어서 사용하기

fun <T> HomeFragment.collectLatestStateFlow(flow: Flow<T>, collect: suspend (T) -> Unit) {
    viewLifecycleOwner.lifecycleScope.launch {
        viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
            flow.collectLatest(collect)
        }
    }
}
    private fun initViewModel() {
//        viewLifecycleOwner.lifecycleScope.launch {
//            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
//                sharedViewModel.searchResults.collectLatest {
//                    homeListAdapter.submitList(it)
//                }
//            }
//        }
        collectLatestStateFlow(sharedViewModel.searchResults) {
            homeListAdapter.submitList(it)
        }
    }
profile
개발 공부 기록 🌱

0개의 댓글