Jetpack Compose调用Spoonacular API:无法存储返回数据求助
问题排查与修复方案
核心问题:Gson解析失败导致数据为null
OkHttp能拿到响应,但ViewModel里的数据为null,本质是API返回的JSON结构与你的Model类不匹配,导致Gson无法正常解析对象,最终返回null。以下是具体问题和修复步骤:
1. Step类缺少必填字段的默认值
Spoonacular API返回的部分步骤(Step)可能没有length字段,但你的Step类中val length: Length没有默认值,Gson解析时会因为找不到该字段抛出异常,导致整个RandomResults对象解析失败。
修复:给length字段添加默认值:
data class Step( val equipment: List<Equipment> = emptyList(), val ingredients: List<Ingredient> = emptyList(), val length: Length = Length(), // 添加默认值 val number: Int = 0, val step: String = "" )
2. Recipe类字段类型与API响应不匹配
servings字段:API返回的是数字(比如4或2.5),用BigDecimal会增加解析复杂度,建议改用Float或Double。instructions字段:Spoonacular的random接口返回的instructions是String类型(不是List),你定义的 List<String>会导致解析失败。
修复:修正Recipe类的字段类型:
data class Recipe( @SerializedName("id") val id: Int, @SerializedName("title") val title: String, @SerializedName("image") val image: String, @SerializedName("servings") val servings: Float, // 改为Float/Double @SerializedName("readyInMinutes") val readyInMinutes: Int, @SerializedName("instructions") val instructions: String?, // 改为String,允许null @SerializedName("analyzedInstructions") val analyzedInstructions: List<AnalyzedInstructions> = emptyList(), @SerializedName("summary") val summary: String = "" )
3. ApiService中getRecipeInfo的路径与参数注解错误
@GET("{id}/information?")末尾的?不需要,Retrofit会自动处理参数拼接,多余的?可能导致请求URL错误。{id}是路径参数,必须用@Path注解,而非@Query,否则请求URL会不符合API规范。
修复:修改接口定义:
@GET("{id}/information") suspend fun getRecipeInfo( @Path("id") id: Int, // 改用@Path注解路径参数 @Query("apiKey") apiKey: String ): Recipe
4. Repository中的冗余代码
RecipeRepository的init块里创建了Retrofit实例但从未使用,直接复用了Api.recipeClient,导致你传入的client没有生效,属于冗余代码。
修复:删除Repository中冗余的Retrofit初始化代码:
class RecipeRepository { // 不需要传入client参数 private val recipeApiService: ApiService = Api.recipeClient // 移除init块 // ... 其他方法保持不变 }
同时ViewModel中也不需要创建OkHttpClient:
class RecipeViewModel(application: Application) : AndroidViewModel(application) { private val recipeRepository: RecipeRepository = RecipeRepository() // 直接实例化 // ... 其他代码保持不变 }
5. ViewModel中的安全空处理
你在getRandomRecipes里用了_randomRecipeResults.value!!.data.toString(),如果解析失败,data会是null,直接用!!会抛出空指针异常(即你截图的错误)。
修复:添加空安全判断:
fun getRandomRecipes(apiKey: String, number: Int) { _randomRecipeResults.value = Resource.Loading() viewModelScope.launch { val result = recipeRepository.getRandomRecipes(apiKey, number) _randomRecipeResults.value = result // 安全打印数据 Log.d("results:", if (result is Resource.Success) result.data?.toString() ?: "Data is null" else result.toString()) } }
额外建议
- 查看完整解析异常日志:在Repository的catch块里打印完整的异常栈信息,快速定位解析失败的具体字段:
catch(e: Exception) { Log.e("RecipeRepository", "解析失败", e) // 打印完整栈轨迹 return Resource.Error("解析失败:${e.message}") }
- 验证API响应结构:直接调用
https://api.spoonacular.com/recipes/random?apiKey=你的密钥&number=1,查看返回的JSON结构,确保Model类的字段和类型完全匹配。
内容的提问来源于stack exchange,提问作者Bartek
相关产品推荐
相关产品推荐

