【问题标题】:How to set up Viewmodel class properly?如何正确设置 Viewmodel 类?
【发布时间】:2021-02-03 00:15:35
【问题描述】:

我正在尝试从 github 学习一些关于 MVVM 模型的教程,但我被困在 viewmodel 类,因为有一个错误说

Not enough information to infer type variable T

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

当我检查我的其他类,如 ApiService、Dao、NetworkBoundResource、ApiResponse、Resources 和 repository 时,一切都很好

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()
}

MovieRespository:

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) })
    }
}

ApiResponse:

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)
}

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

viewModel Class Error

【问题讨论】:

    标签: kotlin mvvm viewmodel


    【解决方案1】:

    您收到错误消息,因为在 when 内部,您正在尝试构造 Resource.Loading() 等的新实例,但这些需要类型,因此它需要类似于 Resource.Loading&lt;Movie&gt;()

    话虽如此,你正在做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&lt;Resource&lt;Movie&gt;&gt;,你需要从map {}返回一个Resource&lt;Movie&gt;(我们可以删除return@map,这只是为了明确)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-10
      • 1970-01-01
      • 1970-01-01
      • 2011-04-15
      • 2020-08-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多