ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Room DB에서 Flow를 사용하여 DB 변경 관찰하기
    개발 2023. 7. 3. 17:16

    Room Db를 사용하여 해당 디비가 업데이트 되었을때마다 바로 이벤트를 받아야하는 경우

     

    구조

    DB Dao

    @Dao
    interface ReportAdDao {
    
        companion object {
            private const val TABLE_NAME = "AD_TABLE"
        }
    
        @Query("SELECT * FROM $TABLE_NAME")
        fun getReportAdList() : Flow<List<ReportAd>>    
    
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        suspend fun insert(reportAd: ReportAd)
    
        @Update
        suspend fun update(reportAd: ReportAd)
    
        @Query("DELETE FROM $TABLE_NAME")
        suspend fun deleteAll()
    
        @Query("DELETE FROM $TABLE_NAME WHERE key=:key")
        suspend fun deleteItem(key: Int)
    
        @Delete
        suspend fun delete(reportAd: ReportAd)
    
    }

    getReportAdList() 쿼리 함수를 flow로 변환해주었다. (기존 : List<ReportAd>) 

    Flow타입으로 바뀌면서 당연히 suspend함수가 빠지게 되었다.(flow는 collect타임에 데이터가 방출되므로..)

    따라서 당연히

    "SELECT * FROM $TABLE_NAME"

    이 수행될때 마다 변경된 결과가 flow로 반환되므로, 이를 수집하여 사용하면 된다.

     

     

    로컬데이터 모듈 : 힐트로 로컬데이터에 의존성을 주입해주기 위한 모듈

    @InstallIn(SingletonComponent::class)
    @Module
    object LocalDataModule {
        @Provides
        fun provideReportAdDao(db: AdNetDatabase): ReportAdDao = db.reportAdDao()
    
        @Provides
        @Singleton
        fun provideDatabase(@ApplicationContext context: Context): AdNetDatabase = Room.databaseBuilder(
            context,
            AdNetDatabase::class.java, "ad_database.db"
        ).build()
    }

    로컬데이터소스의 구현체(중요)

    @FlowPreview
    class LocalDataSourceImpl constructor(
        private val reportAdDao: ReportAdDao,
    ) : LocalDataSource {
        override fun getAllReports() = flow {
            reportAdDao.getReportAdList().collect { emit(it) }
        }
        override suspend fun addReport(reportAd: ReportAd) = reportAdDao.insert(reportAd)
        override suspend fun removeReport(reportAd: ReportAd) = reportAdDao.delete(reportAd)
    }

    이곳에서 getAllReports()를 호출할때마다 Dao의 flow를 수집하고있다. 

     

    Dao.getAllReports() 메소드에서 반환한 Flow를 collect하여 값이 바뀔 때마다 collect block 내부에서 그 결과를 emit하게되므로 이를 통해 View의 변화에 상관 없이 Room DB의 값이 바뀔 때마다 바뀐 값이 emit되어

    ViewModel의 StateFlow에 전달되고, observe하는곳에서 항상 최신값을 옵저빙할수 있게된다

     

     

    리포지토리 impl (구현체)

    class RetryReportRepositoryImpl @Inject constructor(
        private val localDataSource: LocalDataSource
    ) : RetryReportRepository {
        override fun getAllReports(): Flow<List<ReportAd>> = localDataSource.getAllReports()
        override suspend fun addReport(reportAd: ReportAd) = localDataSource.addReport(reportAd)
        override suspend fun removeReport(reportAd: ReportAd) = localDataSource.removeReport(reportAd)
    }

    즉, 리포지토리에서 해당함수를 호출할때마다 dao의 쿼리를 수행하고 이는 플로우에의해 collect되어 수집되게된다.

     

    레포지토리 모듈(힐트에서 레포지토리(로컬데이터)를 제공하기위한 모듈)

    @Singleton
    @Provides
    fun provideRetryReportRepository(
        @Local localDataSource: LocalDataSourceImpl
    ): RetryReportRepository {
        return RetryReportRepositoryImpl(localDataSource)
    }

    뷰모델

    // 광고 실패시 재전송 목록
    val updateReportAdList: StateFlow<List<ReportAd>> = getRetryReportUseCase()
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(TIMEOUT_CONNECT),
            initialValue = listOf()
        )

    해당 데이터를 stateIn으로 StateFlow로 변환하여 해당이벤트가 업데이트될때마다 갱신하고있다.(Rx의 BehaviorSubject와 동일)

     

    Dao.getAllReports() 메소드에서 반환한 Flow를 collect하여 값이 바뀔 때마다 collect block 내부에서 그 결과를 emit하게되므로 이를 통해 View의 변화에 상관 없이 Room DB의 값이 바뀔 때마다 바뀐 값이 emit되어

    ViewModel의 StateFlow에 전달되고, observe하는곳에서 항상 최신값을 옵저빙할수 있게된다

     

Designed by Tistory.