JSON解析偶发NullPointerException问题排查求助
解决JSON解析与网络请求崩溃问题
问题背景
我使用以下Kotlin代码解析JSON文件更新应用内容,Google Play Console显示该代码偶尔崩溃(崩溃率4%),但多台设备测试均无错误。同时我疑惑:是否存在val stationAPP = gson.fromJson(body, StationAPP::class.java)未抛出异常,但JSON加载错误导致部分字段为空的情况?
后续补充报错信息,崩溃触发NullPointerException,定位到代码第1390行的toast.show()。
核心代码
fun fetchJson() { if (isNetworkAvailable()) { val request = Request.Builder().url(stationAPPJsonURL).build() client.newCall(request).enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { val body = response.body?.string() val gson = GsonBuilder().create() try { val stationAPP = gson.fromJson(body, StationAPP::class.java) // ACTIE actie_zichtbaar = stationAPP.actie_zichtbaar actie_img = stationAPP.actie_img actie_url = stationAPP.actie_url // 省略部分代码 } catch (error: JsonParseException) { runOnUiThread { val toast = Toast.makeText(applicationContext, "Problem loading JSON. Functionality may be limited for a while.", Toast.LENGTH_SHORT) val view = toast.view!!.findViewById<TextView>(android.R.id.message) toast?.let { view.gravity = Gravity.CENTER } toast.setGravity(Gravity.CENTER, 0, 0) toast.show() } } } override fun onFailure(call: Call, e: IOException) { runOnUiThread { val toast = Toast.makeText(applicationContext, "Problem loading JSON. Functionality may be limited for a while.", Toast.LENGTH_SHORT) val view = toast.view!!.findViewById<TextView>(android.R.id.message) view?.let { view.gravity = Gravity.CENTER } toast.setGravity(Gravity.CENTER, 0, 0) toast.show() // --> line 1390!! } } }) } }
崩溃日志
Exception java.lang.NullPointerException: at com.familiekoning.grolloo.MainActivity$fetchJson$1.onFailure$lambda-10 (MainActivity.kt:1390) at com.familiekoning.grolloo.MainActivity$fetchJson$1.$r8$lambda$lczurdfMtnZdFDn9iwHvPqw9_Pg at com.familiekoning.grolloo.MainActivity$fetchJson$1$$ExternalSyntheticLambda8.run at android.os.Handler.handleCallback (Handler.java:938) at android.os.Handler.dispatchMessage (Handler.java:99) at android.os.Looper.loop (Looper.java:250) at android.app.ActivityThread.main (ActivityThread.java:7806) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:958)
问题分析与修复方案
1. 崩溃原因定位
崩溃源于onFailure方法中的空指针异常,具体触发点:
Toast.makeText在系统资源紧张、applicationContext失效等极端场景下可能返回null,但代码中直接使用toast.view!!(非空断言)和toast.setGravity,一旦toast为null就会触发NullPointerException。
2. Gson解析字段为空的情况
确实存在Gson解析不抛异常但字段为空的场景:
- JSON缺失对应字段,且
StationAPP类的字段为可空类型(未设置默认值); - JSON字段类型不匹配,但Gson启用了宽松模式(
lenient=true),会自动将不匹配的值设为null或默认值; response.body?.string()返回null,但Gson解析null时会生成StationAPP实例,所有字段为默认值或null。
3. 具体修复步骤
修复空指针崩溃
将Toast相关代码改为安全调用,避免非空断言,同时抽成统一方法减少重复:
private fun showErrorToast() { runOnUiThread { val toast = Toast.makeText(applicationContext, "Problem loading JSON. Functionality may be limited for a while.", Toast.LENGTH_SHORT) toast?.let { val view = it.view?.findViewById<TextView>(android.R.id.message) view?.gravity = Gravity.CENTER it.setGravity(Gravity.CENTER, 0, 0) it.show() } } }
之后在catch块和onFailure方法中直接调用showErrorToast()即可。
优化Gson解析逻辑
- 解析前先判断
body是否为空,提前处理异常:
val body = response.body?.string() ?: run { showErrorToast() return@onResponse }
- 给
StationAPP字段设置默认值或标记为可空,避免空字段引发后续问题:
data class StationAPP( val actie_zichtbaar: Boolean = false, val actie_img: String? = null, val actie_url: String? = null // 其他字段 )
- 启用Gson严格模式,强制解析错误抛出异常,便于排查问题:
val gson = GsonBuilder() .setLenient(false) .create()
4. 额外优化建议
- 在
onFailure中添加日志记录,便于追踪网络失败的具体原因:
override fun onFailure(call: Call, e: IOException) { Log.e("MainActivity", "JSON fetch failed", e) showErrorToast() }
- 检查
isNetworkAvailable()实现的可靠性,避免误判网络状态; - 当Activity可能销毁时,使用弱引用持有上下文,避免内存泄漏或无效上下文导致的问题。
内容的提问来源于stack exchange,提问作者Patrick Koning
相关产品推荐
相关产品推荐

