-
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하는곳에서 항상 최신값을 옵저빙할수 있게된다
'개발' 카테고리의 다른 글
com.google.firebase.provider.FirebaseInitProvider: android.content.pm.PackageManager$NameNotFoundException 오류 (0) 2023.08.09 안드로이드 스튜디오 sha1 인증키 구하기 (0) 2023.08.03 외부라이브러리 proguard 설정 (0) 2023.07.12 Compose에서 Android UI계층 포함하기 (0) 2023.07.06 Android 의존성 주입 Hilt - 3 (0) 2023.07.04