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
相关产品推荐
相关产品推荐

