Сохранение удаленных данных отдельно связанных таблиц при использовании NetworkBoundRepository с сопрограммами (android)

Я хочу использовать в своем приложении принцип единого источника истины. Как добавить несколько таблиц при использовании NetworkBoundRepository.

MainApi.kt

interface MainApi {
    @GET("main")
    suspend fun getMain(): Response<MainResponse>
}

MainResponse.kt

@JsonClass(generateAdapter = true)
data class MainResponse(
        @Json(name = "categories") val categoryList: List<Category>,
        @Json(name = "locations") val locationList: List<Location>,
        @Json(name = "tags") val tagList: List<Tag>
) 

NetworkBoundRepository.kt

@ExperimentalCoroutinesApi
abstract class NetworkBoundRepository<RESULT, REQUEST> {

    fun asFlow() = flow<Resource<RESULT>> {
        emit(Resource.Success(fetchFromLocal().first()))
        val apiResponse = fetchFromRemote()
        val remoteCategories = apiResponse.body()

        if (apiResponse.isSuccessful && remoteCategories != null) {
            saveRemoteData(remoteCategories)
        } else {
            emit(Resource.Failed(apiResponse.message()))
        }

        emitAll(
            fetchFromLocal().map {
                Resource.Success<RESULT>(it)
            }
        )
    }.catch { e ->
        emit(Resource.Failed("Network error! Can't get latest categories."))
    }

    @WorkerThread
    protected abstract suspend fun saveRemoteData(response: REQUEST)

    @MainThread
    protected abstract fun fetchFromLocal(): Flow<RESULT>

    @MainThread
    protected abstract suspend fun fetchFromRemote(): Response<REQUEST>
}

MainRepository.kt

@ExperimentalCoroutinesApi
class MainRepository @Inject constructor(
    private val mainApi: MainApi,
    private val categoryDao: CategoryDao,
    private val locationDao: LocationDao,
    private val tagDao: TagDao
) {
        suspend fun getMain(): Flow<Resource<List<Category>>> {
        return object : NetworkBoundRepository<List<Category>, List<Category>>() {
            override suspend fun saveRemoteData(response: List<Category>) = categoryDao.insertList(response)
            override fun fetchFromLocal(): Flow<List<Category>> = categoryDao.getList()
            override suspend fun fetchFromRemote(): Response<List<Category>> = mainApi.getMain()
        }.asFlow()
    }
}

В настоящее время NetworkBoundRepository и MainRepository работают только с категориями. Я хочу получить некоторые данные из Интернета и сохранить их в связанных таблицах в базе данных. Сначала приложение должно быть офлайн. Как я могу добавить locationDao, tagDao в MainRepository?


person Benfactor    schedule 07.03.2021    source источник


Ответы (1)


Я не совсем понимаю ваш вопрос. Вы уже добавляете locationDao и tagDao в MainRepository:

class MainRepository @Inject constructor(
    ...
    private val locationDao: LocationDao,
    private val tagDao: TagDao
)

Если вы спрашиваете, как предоставить их, чтобы они могли быть введены через Dagger2, вы должны либо определить конструктор dao как @Inject, либо добавить аннотированные методы @Provides или @Binds с соответствующим типом возврата к необходимому @Module и запутать их в одном и том же @Scope - подробнее здесь

Если вы спрашиваете, как использовать эти репозитории в своих функциях, тоже легко:

object : NetworkBoundRepository<List<Category>, MainResponse>() {
            override suspend fun saveRemoteData(response: MainResponse) = response?.run{
                categoryDao.insertList(categoryList)
                locationDao.insertList(locationList)
                tagDao.insertList(tagList)
            }
            override fun fetchCategoriesFromLocal(): Flow<List<Category>> = categoryDao.getList()
            override fun fetchLocationsFromLocal(): Flow<List<Location>> = locationDao.getList()
            override fun fetchTagsFromLocal(): Flow<List<Tag>> = tagDao.getList()
            override suspend fun fetchFromRemote(): Response<MainResponse> = mainApi.getMain()

//This function is not tested and written more like a pseudocode
            override suspend fun mapFromLocalToResponse(): Flow<MainResponse> = fetchCategoriesFromLocal().combine(fetchLocationsFromLocal(), fetchTagsFromLocal()){categories, locations, tags ->
                MainResponse(categories,locations,tags)
            }
        }

Возможно, потребуются еще какие-то корректировки. Но основная проблема вашего кода заключается в том, что вы пытаетесь объединить все разные объекты в одно репо, и это не очень хорошо (и запрос, который возвращает все материалы в одном ответе, тоже не подходит) - я бы предложил разделить это как-то не перепутать все.

person Pavlo Ostasha    schedule 10.03.2021
comment
Спасибо, мне это помогает. Как я могу получить категории, местоположения, теги из локальной БД как MainResponse? Объедините все в один отзыв. Flow ‹MainResponse›. Как оператор rxjava.zip. Я думаю, что в flow.zip есть только два - person Benfactor; 10.03.2021
comment
Можете ли вы поделиться протестированным кодом, чтобы получить несколько таблиц и объединить их в MainResponse? - person Benfactor; 10.03.2021
comment
Что ж, эта функция combine должна работать хорошо .. единственное, что вам, вероятно, нужно будет добавить к ней оператор flow.last() - person Pavlo Ostasha; 10.03.2021
comment
И, кстати, вы можете цеплять zip вот так categories.zip(locations.zip(tags){loc, tag -> loc to tag}){cat, locAndTag-> MainResponse(cat,locAndTag.first,locAndTag.second) } - person Pavlo Ostasha; 10.03.2021