使用Retrofit调用PokeAPI返回Null但Postman正常的问题排查
问题描述
尝试用Retrofit对接PokeAPI,调用pokemon-species接口时返回Null,但该接口在Postman中请求正常。相关代码如下:
Retrofit客户端代码
object RetrofitClient { private const val BASE_URL = "https://pokeapi.co/api/v2/" private val retrofit: Retrofit by lazy { Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() } fun <T> createService(serviceClass: Class<T>): T { return retrofit.create(serviceClass) } }
API接口定义
interface PokeApiService { @GET("pokemon/{id}") fun getPokemon(@Path("id") pokemonId: Int): Response<Pokemon> @GET("pokemon-species") fun getMaxPokemonCount(): Response<PokemonSpeciesResponse> }
数据类定义
data class Pokemon( val name: String, val types: List<PokemonType> ) data class PokemonType( val slot: Int, val type: Type ) data class Type( val name: String ) data class PokemonSpeciesResponse( val count: Int )
返回Null的调用代码
private fun getMaxPokemonCount(): Int? { val response = pokeApiService.getMaxPokemonCount() try { if (response.isSuccessful) { Log.d("Test", "Response: $response") val count = response.body()?.count return count } } catch (e: Exception) { Log.d("Test", "Status code: ${response.code()}") } return null }
Postman返回示例
{ "count": 1017, "next": "https://pokeapi.co/api/v2/pokemon-species?offset=20&limit=20", "previous": null, "results": [ { "name": "bulbasaur", "url": "https://pokeapi.co/api/v2/pokemon-species/1/" }, { "name": "ivysaur", "url": "https://pokeapi.co/api/v2/pokemon-species/2/" }, ... ] }
问题分析与解决办法
核心问题
- 主线程执行网络请求:Android禁止在主线程发起同步网络请求,直接调用
pokeApiService.getMaxPokemonCount()会触发NetworkOnMainThreadException,且该异常发生在try块之外,无法被捕获,导致方法直接跳转到最后返回null。 - 异常捕获范围错误:网络请求调用语句不在try块内,即使发生异常也无法被捕获处理。
修正方案
方案1:使用Kotlin协程实现异步请求(推荐)
1. 修改API接口,添加suspend关键字
interface PokeApiService { @GET("pokemon/{id}") suspend fun getPokemon(@Path("id") pokemonId: Int): Response<Pokemon> @GET("pokemon-species") suspend fun getMaxPokemonCount(): Response<PokemonSpeciesResponse> }
2. 改为挂起函数调用(在协程作用域内执行)
private suspend fun getMaxPokemonCount(): Int? { return try { val response = pokeApiService.getMaxPokemonCount() if (response.isSuccessful) { Log.d("Test", "Response: $response") response.body()?.count } else { Log.d("Test", "Status code: ${response.code()}") null } } catch (e: Exception) { Log.d("Test", "Error: ${e.message}") null } }
调用示例(在Activity/Fragment的生命周期协程中):
lifecycleScope.launch { val maxCount = getMaxPokemonCount() // 处理拿到的宝可梦总数 }
方案2:使用Retrofit异步回调
1. 修改API接口方法返回Call类型
interface PokeApiService { @GET("pokemon/{id}") fun getPokemon(@Path("id") pokemonId: Int): Call<Pokemon> @GET("pokemon-species") fun getMaxPokemonCount(): Call<PokemonSpeciesResponse> }
2. 用enqueue发起异步请求
private fun getMaxPokemonCount() { pokeApiService.getMaxPokemonCount().enqueue(object : Callback<PokemonSpeciesResponse> { override fun onResponse(call: Call<PokemonSpeciesResponse>, response: Response<PokemonSpeciesResponse>) { if (response.isSuccessful) { val count = response.body()?.count Log.d("Test", "Count: $count") // 处理拿到的总数 } else { Log.d("Test", "Status code: ${response.code()}") } } override fun onFailure(call: Call<PokemonSpeciesResponse>, t: Throwable) { Log.d("Test", "Error: ${t.message}") } }) }
额外验证
数据类PokemonSpeciesResponse定义正确,Gson会自动忽略JSON中多余的字段(如next、previous、results),无需修改。
内容的提问来源于stack exchange,提问作者Kalaitos Ornitier
相关产品推荐
相关产品推荐

