简体   繁体   English

如何在Usecase android中使用本地和远程

[英]how to use local and remote in Usecase android

I study domain in android programming and I have a question about that.我在 android 编程中学习领域,我对此有疑问。 Now, I have a getReportFromRemoteUsecase and getReportFromLocalUsecase.现在,我有一个 getReportFromRemoteUsecase 和 getReportFromLocalUsecase。 When the user refreshes UI, we call RemoteUseCase and get data from the remote, and store data in local.当用户刷新 UI 时,我们调用 RemoteUseCase 从远程获取数据,并将数据存储在本地。 We return Flow from local and every change collect.我们从本地返回 Flow 并收集每个更改。 How can use one Usecase for handle both of them in one Usecase.如何使用一个用例在一个用例中处理它们。 For example like this:例如像这样:

operator fun invoke() : Flow<T>{ 
  Repo.getFromRemote()
  return repo.getfromlocal()
 }

But this way has a problem.但是这种方式有一个问题。 Because every time we want to update data we should return flow.因为每次我们想要更新数据时,我们都应该返回流。 I want just a one-time return flow and another time just update from the remote.我只想要一次返回流,而另一次只需要从远程更新。

Okay, from what I understand from the question you are trying to figure out how to use a Local/Remote approach to fetch data, right?好的,根据我从问题中了解到的情况,您正在尝试弄清楚如何使用本地/远程方法来获取数据,对吗?

This approach is mostly useful if you are working on an off-line first app.如果您正在开发离线第一个应用程序,则此方法最有用。 There are many recipes out there, but what I usually do is something like this:那里有很多食谱,但我通常做的是这样的:

class EmployeeRepository(
    private val remoteApi: RemoteRetrofitEmployeeApi,
    private val localDao: LocalRoomDao,
) {
    fun getEmployees(): Flow<ResultData<List<Employee>>> {
        // We create two flows here a remoteFlow that will fetch the data from a Retrofit interface
        // it will parse the data and sabe it in the local cache, here we are using a Room DAO as 
        // local cache
        val remoteFlow = flow {
            try {
                val retrofitResponse = remoteApi.getEmployees()
                val employeesDto = retrofitResponse.body()?.data?.asEmployeeDto()
                if (employeesDto != null) {
                    localDao.storeEmployees(employeesDto)
                }
                emit(ResultData.remote(
                    data = employeesDto?.asEmployeeModel(),
                    error = if (!retrofitResponse.isSuccessful) ResultData.Error.UNKNOWN else ResultData.Error.NO_ERROR_SET
                ))
            } catch (e: Throwable) {
                emit(ResultData.remote(
                    error = ResultData.Error.NO_CONNECTIVITY
                ))
            }
        }
        // Then we create a query on the Room DAO that will actively listen for any changes on the 
        // DB
        val localFlow = localDao.getAllEmployees()
            .map {
                it.asEmployeeModel()
            }
            .map {
                ResultData.local(it)
            }

        // Then we just zip both flows together
        return flowOf(localFlow, remoteFlow).flattenMerge()
    }
}

This is how the Retrofit Interface looks like:这是 Retrofit 界面的样子:

interface RemoteRetrofitEmployeeApi {
    @GET("employees")
    suspend fun getEmployees(): Response<ApiEnvelopeResponse<List<ApiEmployeeResponse>>>

    @GET("employee/{id}")
    suspend fun getEmployee(
        @Path("id") id: Int
    ): Response<ApiEnvelopeResponse<ApiEmployeeResponse>>
}

This is how the DAO looks like:这就是 DAO 的样子:


@Dao
abstract class LocalRoomDao {
    @Query("SELECT * FROM db_employee_dto")
    abstract fun getAllEmployees(): Flow<List<DbEmployeeDto>>

    @Query("SELECT * FROM db_employee_dto where id = :id")
    abstract fun getEmployee(id: Int): Flow<DbEmployeeDto>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract fun storeEmployees(
        employees: List<DbEmployeeDto>
    )

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract fun storeEmployee(
        employees: DbEmployeeDto
    )
}

And notice that there is a Resource wrapper there that we use to keep track from where the data is coming from:并注意那里有一个Resource包装器,我们用它来跟踪数据的来源:

data class ResultData<T>(
    val data: T?,
    val source: Source,
    val error: Error = Error.NO_ERROR_SET
) {
    val isError: Boolean
        get() = error != Error.NO_ERROR_SET

    enum class Error {
        NO_CONNECTIVITY,
        NOT_SUCCESSFUL,
        UNKNOWN,
        NO_ERROR_SET
    }

    enum class Source {
        REMOTE,
        LOCAL
    }

    companion object {
        fun <T> local(data: T): ResultData<T> {
            return ResultData(data, Source.LOCAL)
        }

        fun <T> remote(data: T? = null, error: Error): ResultData<T> {
            return ResultData(data, Source.REMOTE, error)
        }
    }
}

This is a very broad answer, to a very broad question – if there's anything more specific that you would like to know, please provide more context.对于一个非常广泛的问题,这是一个非常广泛的答案——如果您想了解更具体的内容,请提供更多背景信息。 But if what you are looking for are the foundations to implement a component that would help you fetch and store cache data, this is it.但是,如果您要寻找的是实现可帮助您获取和存储缓存数据的组件的基础,那么就是这样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM