You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

额外建议

  1. 查看完整解析异常日志:在Repository的catch块里打印完整的异常栈信息,快速定位解析失败的具体字段:
catch(e: Exception) {
    Log.e("RecipeRepository", "解析失败", e) // 打印完整栈轨迹
    return Resource.Error("解析失败:${e.message}")
}
  1. 验证API响应结构:直接调用https://api.spoonacular.com/recipes/random?apiKey=你的密钥&number=1,查看返回的JSON结构,确保Model类的字段和类型完全匹配。

内容的提问来源于stack exchange,提问作者Bartek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 06:45:07