提交至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 } }
问题原因
- 行号不匹配的原因:Kotlin代码编译后会生成对应的Java字节码文件,Firebase崩溃日志显示的是编译后Java文件的行号,和原Kotlin文件行号不一致属于正常现象,无需纠结,重点看异常类型和调用栈逻辑。
- 空指针异常的根源:调用
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
相关产品推荐
相关产品推荐

