如何正确设置 Viewmodel class?

How to set up Viewmodel class properly?

我正在尝试遵循 github 中关于 MVVM 模型的一些教程,但我被困在 viewmodel class 因为有一个错误说

Not enough information to infer type variable T

Type mismatch.
Required:Resource<Movie>
Found:Unit

当我检查其他 class 时,例如 ApiService、Dao、NetworkBoundResource、ApiResponse、Resources 和 respository 一切都很好

API服务:

interface ApiService {
    @GET("3/movie/popular")
    fun getMyMovie(@Query("api_key") api : String = "32bbbffe944d16d1d2a3ee46cfc6aaa0"
    ) : Flow<ApiResponse<MovieResponse.Movie>>
}

电影道:

@Dao
interface MovieDao : BaseDao<Movie> {

//    @Insert(onConflict = OnConflictStrategy.REPLACE)
//    fun insertMovie(movie: List<Movie>)

    @Query("SELECT * FROM `movie` ORDER by movie_id DESC")
    fun getMyMovie() : Flow<Movie>

    @Query("SELECT * FROM `movie` ORDER by movie_id DESC")
    fun findAllMovie() : Maybe<List<Movie>>

    @Query("SELECT * FROM `movie` ORDER by movie_id DESC")
    fun streamAll() : Flowable<List<Movie>>

    @Query("DELETE FROM `movie`")
    fun deleteAll()
}

电影资源库:

class MovieRespository (val apiService: ApiService, val movieDao: MovieDao) {

    fun getListMovie() : Flow<Resource<Movie>> {
        return networkBoundResource(
                fetchFromLocal = { movieDao.getMyMovie() },
                shouldFetchFromRemote = {true},
                fetchFromRemote = {apiService.getMyMovie()},
                processRemoteResponse = {},
                saveRemoteData = {movieDao.insert(
                        it.results.let {
                            it.map { data -> Movie.from(data) }
                        }
                )},
                onFetchFailed = {_, _ ->}
        ).flowOn(Dispatchers.IO)
    }

NeteorkBoundResource:

inline fun <DB, REMOTE> networkBoundResource(
        crossinline fetchFromLocal: () -> Flow<DB>,
        crossinline shouldFetchFromRemote: (DB?) -> Boolean = { true },
        crossinline fetchFromRemote: () -> Flow<ApiResponse<REMOTE>>,
        crossinline processRemoteResponse: (response: ApiSuccessResponse<REMOTE>) -> Unit = { Unit },
        crossinline saveRemoteData: (REMOTE) -> Unit = { Unit },
        crossinline onFetchFailed: (errorBody: String?, statusCode: Int) -> Unit = { _: String?, _: Int -> Unit }
) = flow<Resource<DB>> {

    emit(Resource.Loading(null))

    val localData = fetchFromLocal().first()

    if (shouldFetchFromRemote(localData)) {

        emit(Resource.Loading(localData))

        fetchFromRemote().collect { apiResponse ->
            when (apiResponse) {
                is ApiSuccessResponse -> {
                    processRemoteResponse(apiResponse)
                    apiResponse.body?.let { saveRemoteData(it) }
                    emitAll(fetchFromLocal().map { dbData ->
                        Resource.Success(dbData)
                    })
                }

                is ApiErrorResponse -> {
                    onFetchFailed(apiResponse.errorMessage, apiResponse.statusCode)
                    emitAll(fetchFromLocal().map {
                        Resource.Error(
                                apiResponse.errorMessage,
                                it
                        )
                    })
                }
            }
        }
    } else {
        emitAll(fetchFromLocal().map { Resource.Success(it) })
    }
}

Api 响应:

sealed class ApiResponse<T> {
    companion object {

        fun <T> create(error: Throwable): ApiErrorResponse<T> {
            return ApiErrorResponse(
                    error.message ?: "Unknown error",
                    0
            )
        }


        fun <T> create(response: Response<T>): ApiResponse<T> {
            return if (response.isSuccessful) {
                val body = response.body()
                val headers = response.headers()
                if (body == null || response.code() == 204) {
                    ApiEmptyResponse()
                } else {
                    ApiSuccessResponse(
                            body,
                            headers
                    )
                }
            } else {
                val msg = response.errorBody()?.string()
                val errorMsg = if (msg.isNullOrEmpty()) {
                    response.message()
                } else {
                    msg
                }
                ApiErrorResponse(
                        errorMsg ?: "Unknown error",
                        response.code()
                )
            }
        }
    }
}

/**
 * separate class for HTTP 204 responses so that we can make ApiSuccessResponse's body non-null.
 */
class ApiEmptyResponse<T> : ApiResponse<T>()

data class ApiSuccessResponse<T>(
        val body: T?,
        val headers: okhttp3.Headers
) : ApiResponse<T>()

data class ApiErrorResponse<T>(val errorMessage: String, val statusCode: Int) : ApiResponse<T>()

资源:

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

//    data class Loading<T>(val loadingData: T?) : Resource<T>(Status.LOADING, loadingData, null)
//    data class Success<T>(val successData: T?) : Resource<T>(Status.SUCCESS, successData, null)
//    data class Error<T>(val msg: String, val error: T?) : Resource<T>(Status.ERROR, error, msg)

    companion object {

        fun <T> Success(data: T?): Resource<T> {
            return Resource(Status.SUCCESS, data,null)
        }

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

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

        }
    }
}

主视图模型:

class MainViewModel(private val movieRespository: MovieRespository) : ViewModel() {
    @ExperimentalCoroutinesApi
    val getListMovies: LiveData<Resource<Movie>> = movieRespository.getListMovie().map {
        when(it.status){
            Resource.Loading() ->{}
            Resource.Success() ->{}
            Resource.Error() ->{}
        }
    }.asLiveData(viewModelScope.coroutineContext)
}

具体来说,这是错误的样子,这是我学习的 link 教程 https://github.com/hadiyarajesh/flower

viewModel Class Error

你得到了错误,因为在 when 中,你正在尝试构建 Resource.Loading() 等的新实例,但是那些需要一个类型,所以它需要类似于 Resource.Loading<Movie>().

也就是说,你正在做 when(it.status),所以 when 中的情况不应该是 Resource.Loading,而是 Status.LOADING

class MainViewModel(private val movieRespository: MovieRespository) : ViewModel() {
    @ExperimentalCoroutinesApi
    val getListMovies: LiveData<Resource<Movie>> = movieRespository.getListMovie().map {
        when(it.status){
            Status.LOADING ->{}
            Status.SUCCESS ->{}
            Status.ERROR ->{}
        }
        return@map it
    }.asLiveData(viewModelScope.coroutineContext)
}

此外,由于您要声明 LiveData<Resource<Movie>>,因此您需要 return 来自 map {}Resource<Movie>(我们可以删除 return@map,它是只是为了明确)