【发布时间】:2021-12-23 02:21:08
【问题描述】:
我正在 Kotlin 中开发一个应用程序,它使用 Retrofit2 库与 PokeApi 连接。
我曾尝试使用 corrutines 来做到这一点,但我开始获得的响应为 null,而在使用 corrutines 之前,通过异步调用。
调用 API 的 dataProvider 的代码如下:
DataProvider.kt
/**
* Metodo que permite obtener un LiveData con la informacion del siguiente pokemon por id, que será la posible evolución.
*/
fun viewPokemonEvolution(id: Long): LiveData<PokemonFormResponse>? {
var remotePokemonEvolutionFormData : LiveData<PokemonFormResponse>? = null
var call: Response<LiveData<PokemonFormResponse>>
var data: LiveData<PokemonFormResponse>?
CoroutineScope(Dispatchers.Main).launch {
call = remoteDataSource.downloadPokemonViewedData(id)
data = call.body()
if(call.isSuccessful){
remotePokemonEvolutionFormData = data
}
}
return remotePokemonEvolutionFormData!!
}
我的 API 类
interface PokemonApi {
@GET("pokemon-form/{id}")
suspend fun getPokemonInfo(@Path("id") idPokemon: Long): Response<LiveData<PokemonFormResponse>>
@GET("pokemon/{name}")
suspend fun getPokemonExtendedInfo(@Path("name") pokemonName: String): Response<LiveData<PokemonExtendedInfoResponse>>
}
我的数据类
PokemoFormResponse.kt
data class PokemonFormResponse(
@SerializedName("form_name")
val formName: String,
@SerializedName("form_names")
val formNames: List<Any>,
@SerializedName("form_order")
val formOrder: Int,
@SerializedName("id")
val id: Int,
@SerializedName("is_battle_only")
val isBattleOnly: Boolean,
@SerializedName("is_default")
val isDefault: Boolean,
@SerializedName("is_mega")
val isMega: Boolean,
@SerializedName("name")
val name: String,
@SerializedName("names")
val names: List<Any>,
@SerializedName("order")
val order: Int,
@SerializedName("pokemon")
val pokemon: PokemonUrl,
@SerializedName("sprites")
val sprites: Sprites,
@SerializedName("version_group")
val versionGroup: VersionGroup
){
fun idFilledWithZero(): String {
return String.format("%03d", id)
}
}
我的远程数据源
IRemoteDataSource.kt
interface IRemoteDataSource {
suspend fun downloadPokemonViewedData(id: Long): Response<LiveData<PokemonFormResponse>>
suspend fun downloadPokemonCatchedData(name: String): Response<LiveData<PokemonExtendedInfoResponse>>
}
interface ILocalDataSource {
fun getPokemonList(): LiveData<List<Pokemon>>
fun getPokemonById(idPokemon: Long): LiveData<Pokemon>
suspend fun insertPokemon(pokemon: Pokemon)
}
以及在LocalDataSource中调用房间的DAO:
@Dao
interface PokemonDao {
@Query("SELECT * from listaPokemon")
fun getAll(): LiveData<List<Pokemon>>
@Insert(onConflict = REPLACE)
fun insert(pokemon:Pokemon)
@Insert(onConflict = REPLACE)
fun insertAll(vararg pokemons: Pokemon)
@Query("SELECT * from listaPokemon WHERE id = :pokemonId")
fun getById(pokemonId: Long): LiveData<Pokemon>
}
我希望你能提出一些修复实现的方法,因为这样更好的方法是使用 corrutines 而不是异步调用。
我添加了一个debugg的捕获显示所有属性为null
希望您能帮上忙,如果是这样,请提前致谢!
[编辑]
添加远程数据源`
class RemoteDataSource : IRemoteDataSource{
val BASE_URL = "https://pokeapi.co/api/v2/"
val TIMEOUT: Long = 30
var apiServices: PokemonApi
init {
val httpClient : OkHttpClient.Builder = OkHttpClient.Builder()
httpClient.connectTimeout(TIMEOUT, TimeUnit.SECONDS)
httpClient.readTimeout(TIMEOUT, TimeUnit.SECONDS)
httpClient.writeTimeout(TIMEOUT, TimeUnit.SECONDS)
val retrofit: Retrofit = Retrofit.Builder()
//Se utiliza el networkIO como ejecutor de Retrofit
.callbackExecutor(AppExecutors.networkIO)
.client(httpClient.build())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
apiServices = retrofit.create(PokemonApi::class.java)
}
/**
* Función tipo utilizando retrofit para obtener datos desde una api remota
*
* Simplicación de las funciones mediante las corrutinas
*/
override suspend fun downloadPokemonViewedData(id: Long): Response<LiveData<PokemonFormResponse>> = withContext(Dispatchers.Main) {
apiServices.getPokemonInfo(
id
)
}
override suspend fun downloadPokemonCatchedData(name: String): Response<LiveData<PokemonExtendedInfoResponse>> = withContext(Dispatchers.Main){
apiServices.getPokemonExtendedInfo(
name
)
}
还有界面
IRemoteDatasource.kt
interface IRemoteDataSource {
suspend fun downloadPokemonViewedData(id: Long): Response<LiveData<PokemonFormResponse>>
suspend fun downloadPokemonCatchedData(name: String): Response<LiveData<PokemonExtendedInfoResponse>>
}
【问题讨论】:
-
你在哪里有 GET 请求?
IRemoteDataSource是做什么的? -
对不起,我已经加了,IRemoteDataSource是RemoteDataSource的接口,只是stablish方法,必须实现
-
suspend fun getPokemonInfo(@Path("id") idPokemon: Long): Response<LiveData<PokemonFormResponse>>为什么要返回包裹在LiveData中的原始回复,然后又返回包裹在Response中的回复。为什么不直接返回PokemonFormResponse? -
主要目标是我需要使用 LiveData 来管理视图模型,而响应包装是因为我将其视为封装调用 API 的最佳方式
-
您可以从 api 返回 PokemonFormResponse 并将其转换为 ViewModel 或 Data Provider 中的实时数据(或流)。 This 文章可能会有所帮助。我认为您无法从这样的改造中获得实时数据(我猜至少没有额外的转换器工厂)