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

Jetpack Compose中Retrofit请求静态JSON文件空指针异常如何解决?

问题根因及修复方案

1. ResponseBody.string() 不可重复调用

ResponseBody的string()方法会一次性读取完网络响应流,读取完成后流会直接关闭,第二次调用只会返回空值或者无效乱码。你现在先调用Log.d(..., responseBody.string())打印内容,之后解析又调用一次responseBody.string(),此时拿到的已经是无效内容,这就是JSON解析失败返回null的直接原因。
修复方案:把string()的结果先存到临时变量里,打印和解析都复用这个临时变量:

val responseStr = responseBody.string()
Log.d("responsebody", responseStr)
val currencyAPIResponse = gson.fromJson(responseStr, CurrencyAPIResponse::class.java)

2. 数据类不能直接使用Compose的MutableState

Gson反序列化仅支持基础数据类型、集合、普通POJO类,无法识别Compose的MutableState类型。你把CurrencyAPIResponse的data字段声明为MutableState类型,Gson反序列化时不知道怎么给这个字段赋值,最终会导致该字段为null,触发空指针异常。
修复方案:数据类只存普通数据类型,后续需要响应式状态时,再把解析出来的普通列表包装成MutableState:

// 修改后的响应数据类
data class CurrencyAPIResponse(
    @SerializedName("data") var data: List<CurrencyRates>
)

data class CurrencyRates (
    @SerializedName("code") var code : String,
    @SerializedName("value") var value : String
)

// ViewModel中解析后再转成MutableState
val currencyList = mutableStateOf(emptyList<CurrencyRates>())
// ...解析完成后赋值
currencyList.value = currencyAPIResponse.data

3. 日志里的杂项不是服务端返回的响应内容

你看到的JSON中插入的[ 1631385414.170 12452:12478 D/responsebody]内容是Logcat本身的日志前缀和换行标记,不是服务端返回的内容,不需要额外处理,按第一条方案修复后,解析用的就是完整正确的JSON。

4. ViewModel函数作用域错误

你当前的getCurrencyFileData()函数定义在了ViewModel类的花括号外面,不属于ViewModel成员,无法访问viewModelScope,也无法被ViewModel的init块调用,需要把函数移到类内部:

@HiltViewModel
class CurrencyDataViewModel
@Inject
constructor(private val currencyService: CurrencyService
) : ViewModel() 
{
    val currencyList = mutableStateOf(emptyList<CurrencyRates>())

    init {
        getCurrencyFileData()
    }

    // 函数放到类内部
    private fun getCurrencyFileData() = viewModelScope.launch(Dispatchers.IO) {
        val url: String = "https://firebasestorage.googleapis.com/v0/b/culoader.appspot.com/o/json%2Fcudata.json?alt=media&token=d0679703-2f6c-440f-af03-d4d61305cc84"
        val responseBody = currencyService.getCurrencyFile(url).body()
        if (responseBody != null) {
            val responseStr = responseBody.string()
            val gson = GsonBuilder().setPrettyPrinting().create()
            val currencyAPIResponse = gson.fromJson(responseStr, CurrencyAPIResponse::class.java)
            currencyAPIResponse.data?.let {
                currencyList.value = it
                Log.d("Data", it[0].code)
            }
        }
    }
}

可选优化:直接让Retrofit完成JSON解析

你已经在Retrofit中添加了GsonConverterFactory,不需要手动拿ResponseBody解析,可以直接让接口返回解析好的响应类,简化代码:

// 服务类修改
interface CurrencyService {
    @GET
    suspend fun getCurrencyFile(@Url fileUrl:String): Response<CurrencyAPIResponse>
}

// ViewModel调用逻辑简化
val response = currencyService.getCurrencyFile(url)
if (response.isSuccessful) {
    response.body()?.data?.let {
        currencyList.value = it
        Log.d("Data", it[0].code)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 07:54:04