【问题标题】:How to set data in Model?如何在模型中设置数据?
【发布时间】:2021-01-08 17:54:47
【问题描述】:

我有一个具有 [id;name;thumb] 的实体“饮料”,我将这些实体用于 2 个响应调用。一个响应返回给我一个 NonAlcohol 饮料列表,另一个AlcoholList,我正在使用 Room 来缓存数据。但是当我运行应用程序时,我看到我的列表合并了,经过一番思考,我找到了解决这个问题的方法,我在我的实体“alcoholStatus”中添加了一个布尔字段。但我不明白如何将数据设置为此变量正确使用此 AccessDataStrategy。我是 Android 新手,这是我的学习项目。请给我正确的方法来解决这个问题。

https://github.com/YaroslavSulyma/LetsDrink/tree/master/app/src/main/java/com/example/letsdrink

非常感谢!

实体

@Entity(tableName = "drinks")
data class DrinksModel(
    @SerializedName("strDrink")
    val strDrink: String,
    @SerializedName("strDrinkThumb")
    val strDrinkThumb: String?,
    @SerializedName("idDrink")
    @PrimaryKey
    val idDrink: Int,
    var alcohol: Boolean
)

数据访问策略代码

fun <T, A> performGetOperation(
    databaseQuery: () -> LiveData<T>,
    networkCall: suspend () -> Resource<A>,
    saveCallResult: suspend (A) -> Unit
): LiveData<Resource<T>> =
    liveData(Dispatchers.IO) {
        emit(Resource.loading())
        val source = databaseQuery.invoke().map { Resource.success(it) }
        emitSource(source)

        val responseStatus = networkCall.invoke()
        if (responseStatus.status == SUCCESS) {
            saveCallResult(responseStatus.data!!)

        } else if (responseStatus.status == ERROR) {
            emit(Resource.error(responseStatus.message!!))
            emitSource(source)
        }
    }

资源

data class Resource<out T>(val status: Status, val data: T?, val message: String?) {

    enum class Status {
        SUCCESS,
        ERROR,
        LOADING
    }

    companion object {
        fun <T> success(data: T): Resource<T> {
            return Resource(Status.SUCCESS, data, null)
        }

        fun <T> error(message: String, data: T? = null): Resource<T> {
            return Resource(Status.ERROR, data, message)
        }

        fun <T> loading(data: T? = null): Resource<T> {
            return Resource(Status.LOADING, data, null)
        }
    }
}

存储库

class CocktailsRepository @Inject constructor(
        private val remoteDataSource: CocktailsRemoteDataSource,
        private val localDataSource: CocktailsDao
) {
        fun getAlcoholicCocktails() = performGetOperation(
            databaseQuery = { localDataSource.getAlcoholicCocktails() },
            networkCall = { remoteDataSource.getAllAlcoholicCocktails()},
            saveCallResult = { localDataSource.insertAllDrinks(it.drinks) }
        )
    
        fun getNonAlcoholicCocktails() = performGetOperation(
            databaseQuery = { localDataSource.getNonAlcoholicCocktails() },
            networkCall = { remoteDataSource.getAllNonAlcoholicCocktails() },
            saveCallResult = { localDataSource.insertAllDrinks(it.drinks) }
        )
    }

@Dao
interface CocktailsDao {
    @Query("SELECT * FROM drinks WHERE alcohol = 'true'")
    fun getAlcoholicCocktails(): LiveData<List<DrinksModel>>

    @Query("SELECT * FROM drinks WHERE alcohol = 'false'")
    fun getNonAlcoholicCocktails(): LiveData<List<DrinksModel>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertAllDrinks(drinks: List<DrinksModel>)
}

远程数据源

class CocktailsRemoteDataSource @Inject constructor(private val iCocktailApisService: ICocktailApisService) :
    BaseDataSource() {

    suspend fun getAllAlcoholicCocktails() =
        getResult { iCocktailApisService.allAlcoholicAndNonAlcoholicCocktails("Alcoholic") }

    suspend fun getAllNonAlcoholicCocktails() =
        getResult { iCocktailApisService.allAlcoholicAndNonAlcoholicCocktails("Non_Alcoholic") }
}

【问题讨论】:

    标签: android kotlin mvvm repository android-room


    【解决方案1】:

    首先:我强烈建议您为远程和本地模型类定义单独的数据类,并在需要时进行它们之间的映射,例如:

    远程数据模型:

    data class DrinkRemoteModel(
        @SerializedName("idDrink")
        val idDrink: Int,
        @SerializedName("strDrink")
        val strDrink: String,
        @SerializedName("strDrinkThumb")
        val strDrinkThumb: String?,
        @SerializedName("alcohol")
        var alcohol: Boolean
    )
    

    本地数据模型:

    @Entity(tableName = "drinks")
    data class DrinkLocalModel(
    @PrimaryKey
        @ColumnInfo(name = "idDrink")
        val idDrink: Int,
        @ColumnInfo(name = "strDrink")
        val strDrink: String,
        @ColumnInfo(name = "strDrinkThumb")
        val strDrinkThumb: String?,
        @ColumnInfo(name = "alcohol")
        var alcohol: Boolean
    )
    

    回到你的实现:我认为导致问题的原因是 Room 将实体中的布尔字段映射到整数列 1 for @ 987654324@ 和 0 用于 false,因此请尝试在您的 DAO 中更改您的查询,如下所示:

    @Dao
    interface CocktailsDao {
        @Query("SELECT * FROM drinks WHERE alcohol = 1")
        fun getAlcoholicCocktails(): LiveData<List<DrinksModel>>
    
        @Query("SELECT * FROM drinks WHERE alcohol = 0")
        fun getNonAlcoholicCocktails(): LiveData<List<DrinksModel>>
    
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        suspend fun insertAllDrinks(drinks: List<DrinksModel>)
    }
    

    或者:您可以将 getAlcoholicCocktailsgetNonAlcoholicCocktails 替换为一个 DAO 函数,如下所示:

    @Query("SELECT * FROM drinks WHERE alcohol = :isAlcoholic")
    fun getCocktails(isAlcoholic : Boolean = true): LiveData<List<DrinksModel>>
    

    【讨论】:

    • 这是一个很好的答案,但我不知道如何以及在哪里可以填写“酒精”字段。 1)从本地数据库恢复数据 2)如果数据库为空,我应该从 API 获取数据 3)将响应中的数据保存到本地数据库 但是 API 不返回酒精状态,我不明白我可以在哪里填写这个字段(它会在哪里正确?在存储库中?在远程模块还是本地?)。我知道这可能是一个愚蠢的问题,但我不知道如何正确地做到这一点。
    • 我相信这个逻辑应该存在于你的存储库中,因为它是负责获取和保存策略的组件
    • 我认为您应该在saveCallResult 内设置您的项目的布尔值,换句话说,您的数据库中的行始终包含正确的alcoholic 布尔值
    • 我试过了,但是这个实现返回 Unit 而不是 List
    • 哇,非常感谢!你是最好的!我们也可以使用 onEach{} 代替 apply{foreach{}}
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多