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

提交至Google Play后Kotlin应用出现NullPointerException问题排查

问题分析与修复方案

问题背景

用Kotlin开发的应用提交Google Play后,Firebase捕获到空指针异常,日志显示异常发生在SteamEpicGamesRecyclerAdapter.java第212行,但实际该适配器是仅65行的Kotlin文件,行号不匹配。

Firebase错误日志

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.trim()' on a null object reference
   at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1838)
   at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
   at java.lang.Double.parseDouble(Double.java:538)
   at com.yousefelsayed.gamescheap.adapter.SteamEpicGamesRecyclerAdapter.onBindViewHolder(SteamEpicGamesRecyclerAdapter.java:212)
   ... // 后续调用栈省略

相关Kotlin代码

适配器的onBindViewHolder方法

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val itemViewModel = mList[position]
    holder.layout.doOnLayout {
        Glide.with(holder.gameImage)
            .load(itemViewModel.image)
            .apply(RequestOptions().override(holder.layout.width,holder.layout.height))
            .skipMemoryCache(true)
            .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
            .format(DecodeFormat.PREFER_ARGB_8888)
            .placeholder(R.color.imageLoadingColor)
            .error(R.drawable.no_image_found)
            .into(holder.gameImage)
    }
    holder.gameImage.setImageDrawable(null)
    holder.gameTitle.text = itemViewModel.title
    if (itemViewModel.x == "0.00") holder.gameCurrent.text = holder.itemView.context.getString(R.string.Free_String)
    else holder.gameCurrent.text = holder.itemView.context.getString(R.string.xText,itemViewModel.x)
    holder.gameOld.text = holder.itemView.context.getString(R.string.xText,itemViewModel.y)
    holder.gameOld.paintFlags = holder.gameOld.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
    val percentage = (itemViewModel.x.toDouble() / itemViewModel.y.toDouble()) * 100
    if (percentage.toString() == "0.0") holder.gamePercentage.text = holder.itemView.context.getString(R.string.hundred_percent)
    else holder.gamePercentage.text = holder.itemView.context.getString(R.string.percentageText,percentage.toInt().toString())
}

ViewModel及Factory代码

data class SteamAndEpicGamesViewModel(private val steamAndEpicGamesRepository: SteamAndEpicGamesRepository): ViewModel(){

private val errorHandler = CoroutineExceptionHandler{_,t ->
    run{
        requestStatus.postValue(t.message.toString())
    }
}
fun startSteamEpicGamesRequest(){
    viewModelScope.launch(Dispatchers.IO+errorHandler) {
        steamAndEpicGamesRepository.getSteamAndEpicGames()
    }
}

val games: LiveData<Games> get() = steamAndEpicGamesRepository.games
val requestStatus = steamAndEpicGamesRepository.requestStatus
}
class SteamAndEpicGamesViewModelFactory @Inject constructor(private val steamAndEpicGamesRepository: SteamAndEpicGamesRepository): ViewModelProvider.Factory {

override fun <T : ViewModel> create(modelClass: Class<T>): T {
    return SteamAndEpicGamesViewModel(steamAndEpicGamesRepository) as T
}
}

问题原因

  1. 行号不匹配的原因:Kotlin代码编译后会生成对应的Java字节码文件,Firebase崩溃日志显示的是编译后Java文件的行号,和原Kotlin文件行号不一致属于正常现象,无需纠结,重点看异常类型和调用栈逻辑。
  2. 空指针异常的根源:调用Double.parseDouble时传入了null字符串,对应到Kotlin代码中,是itemViewModel.x或itemViewModel.y为null,直接调用toDouble()扩展函数触发了空指针(Kotlin的toDouble()是String类型的扩展,若调用者为null则会抛出NPE)。

修复方案

1. 字符串转Double时增加空安全处理

修改百分比计算逻辑,用安全调用和Elvis操作符处理null值,同时避免除以0的情况:

// 处理x和y的null情况,默认值设为0.0
val xValue = itemViewModel.x?.toDouble() ?: 0.0
val yValue = itemViewModel.y?.toDouble() ?: 0.0
// 避免除以0,若y为0则百分比设为0(或根据业务逻辑调整)
val percentage = if (yValue == 0.0) 0.0 else (xValue / yValue) * 100

2. 优化价格显示的判空逻辑

当itemViewModel.x为null时,也显示“免费”,同时处理y为null的情况:

// 若x为空或等于0.00,显示免费
if (itemViewModel.x.isNullOrEmpty() || itemViewModel.x == "0.00") {
    holder.gameCurrent.text = holder.itemView.context.getString(R.string.Free_String)
} else {
    holder.gameCurrent.text = holder.itemView.context.getString(R.string.xText, itemViewModel.x)
}

// 若y为null,显示默认值(如0.00)
holder.gameOld.text = holder.itemView.context.getString(R.string.xText, itemViewModel.y ?: "0.00")

3. 从数据源层面避免null值

检查SteamAndEpicGamesRepository中解析接口数据的逻辑,确保x和y字段不会被解析为null,比如在数据模型类中给这两个字段设置默认值:

// 假设Games数据类中的字段定义
data class GameItem(
    val title: String,
    val image: String?,
    val x: String = "0.00", // 设置默认值
    val y: String = "0.00"
)

内容的提问来源于stack exchange,提问作者YOUSEF -ELSAYED

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 12:30:54