You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于MVVM与RxJava的Repository数据加载逻辑优化咨询(Android Jetpack Navigation项目)

Hey there! Your current logic gets the job done, but we can refine it to be more reactive, robust, and aligned with modern Android Jetpack best practices. Let’s break down a better approach that leverages single source of truth principles and reactive streams (using Kotlin Coroutines/Flow instead of RxJava, though we’ll touch on RxJava adjustments too).

Key Issues with Your Current Implementation

While functional, your setup has a few potential gaps:

  • You’re manually managing Disposable instances, which can lead to leaks if not handled carefully.
  • The getDataFromDBJobMissions() call might execute before makeApiCallAndSaveToDBJobMissions() finishes (since it’s asynchronous), leading to temporary empty data in the UI.
  • There’s no built-in support for loading/error states, which are critical for good UX.

Optimized Solution Using Kotlin Flow & Room

Room natively supports returning Flow for queries, which automatically emits updates whenever the underlying database changes. This lets us build a reactive pipeline where the UI always gets the latest data without manual refreshes.

Step 1: Update Your DAO

Replace the count query with a direct list query that returns a Flow:

@Dao
interface MissionDao {
    @Query("SELECT * FROM MissionsTable")
    fun getJobMissions(): Flow<List<Mission>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertAll(missions: List<Mission>)
}

Step 2: Refine the Repository

We’ll create a single reactive stream that prioritizes local data, fetches remote data only if local is empty, and automatically propagates database updates to the UI:

// First, define a sealed class to handle loading/success/error states
sealed class Result<out T> {
    object Loading : Result<Nothing>()
    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val message: String) : Result<Nothing>()
}

class MissionRepository(
    private val dao: MissionDao,
    private val missionApi: MissionApi // Your remote API service
) {
    // Single source of truth: UI observes this Flow for all data updates
    fun getCurrentJobMissions(): Flow<Result<List<Mission>>> = flow {
        // Emit loading state first (optional but helpful for UX)
        emit(Result.Loading)

        // Fetch initial local data
        val localMissions = dao.getJobMissions().first()
        emit(Result.Success(localMissions))

        // If local data is empty, fetch from remote and save to DB
        if (localMissions.isEmpty()) {
            try {
                val remoteMissions = missionApi.fetchJobMissions()
                dao.insertAll(remoteMissions)
                // No need to emit here: Room's Flow will auto-emit the updated local data
            } catch (e: Exception) {
                emit(Result.Error(e.message ?: "Failed to fetch missions"))
            }
        }
    }.flowOn(Dispatchers.IO)

    // Add a refresh method for pull-to-refresh scenarios
    suspend fun refreshJobMissions() {
        val remoteMissions = missionApi.fetchJobMissions()
        dao.insertAll(remoteMissions)
    }
}

Step 3: Integrate with ViewModel

Use ViewModel and StateFlow to manage the data lifecycle and ensure the UI only receives relevant updates:

class MissionViewModel(
    private val repository: MissionRepository
) : ViewModel() {
    private val _missionsState = MutableStateFlow<Result<List<Mission>>>(Result.Loading)
    val missionsState: StateFlow<Result<List<Mission>>> = _missionsState

    init {
        viewModelScope.launch {
            repository.getCurrentJobMissions().collect { result ->
                _missionsState.value = result
            }
        }
    }

    fun onRefresh() {
        viewModelScope.launch {
            try {
                repository.refreshJobMissions()
            } catch (e: Exception) {
                _missionsState.value = Result.Error(e.message ?: "Refresh failed")
            }
        }
    }
}

If You Prefer RxJava

If you want to stick with RxJava instead of Coroutines, you can adjust the DAO and repository like this:

// Updated DAO for RxJava
@Dao
interface MissionDao {
    @Query("SELECT * FROM MissionsTable")
    fun getJobMissions(): Observable<List<Mission>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertAll(missions: List<Mission>): Completable
}

// RxJava Repository
class MissionRepository(
    private val dao: MissionDao,
    private val missionApi: MissionApi
) {
    fun getCurrentJobMissions(): Observable<Result<List<Mission>>> {
        return dao.getJobMissions()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .flatMap { localMissions ->
                if (localMissions.isEmpty()) {
                    missionApi.fetchJobMissions()
                        .flatMapCompletable { remoteMissions ->
                            dao.insertAll(remoteMissions)
                        }
                        .andThen(dao.getJobMissions())
                        .map { Result.Success(it) }
                        .startWithItem(Result.Loading)
                        .onErrorReturn { Result.Error(it.message ?: "Failed to fetch") }
                } else {
                    Observable.just(Result.Success(localMissions))
                }
            }
    }
}

Why This Is Better

  • Single Source of Truth: The UI only observes one stream, eliminating data inconsistency.
  • Automatic Updates: Room’s Flow/Observable emits new data whenever the database is updated, so the UI refreshes automatically after saving remote data.
  • Clean State Management: The Result sealed class makes it easy to handle loading, success, and error states in the UI.
  • Lifecycle Safety: Using viewModelScope ensures coroutines are canceled when the ViewModel is destroyed, preventing leaks.

内容的提问来源于stack exchange,提问作者Shqipdon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 03:39:09