安卓应用启动崩溃(<App Name>已停止)问题排查求助
应用启动崩溃(NullPointerException)排查与修复
问题概况
应用启动即崩溃,提示「The MainActivityViewModel.kt第24行的NullPointerException引发,导致无法启动MainActivity。
报错根源分析
查看MainActivityViewModel.kt第24行代码:
timer = object : CountDownTimer(timerValue.value!!.toLong(),1000) {
核心问题:
timerValue是MutableLiveData<Long>,初始化时未设置默认值,默认value为null- 使用
!!强制非空拆包,当timerValue.value为null时直接抛出NPE MainActivity的onCreate方法中直接调用mAVieModel.startTimer(),此时用户尚未输入任何值,timerValue必然为空
另外存在潜在风险:timer用lateinit声明,但未做初始化判断,若用户先点击Stop按钮,会触发另一个NPE;原代码中CountDownTimer创建后未调用start(),即使修复NPE,定时器也不会运行。
修复方案
1. 给timerValue设置默认初始值
在ViewModel中初始化时为timerValue赋值,避免空值:
var timerValue = MutableLiveData<Long>(0L)
2. 安全处理timerValue的取值
修改startTimer方法,避免强制拆包,增加空值判断,同时补充定时器启动方法:
fun startTimer(){ val targetTime = timerValue.value ?: return // 先取消已有定时器,避免重复创建 if(::timer.isInitialized){ timer.cancel() } timer = object : CountDownTimer(targetTime,1000) { override fun onTick(millisUntilFinished: Long) { val remainingTime = millisUntilFinished/1000 remaining_seconds.value = remainingTime.toInt() } override fun onFinish() { _finished.value = true } }.start() }
3. 移除Activity中提前调用的startTimer
删除MainActivity中onCreate里的mAVieModel.startTimer(),仅在用户点击Start按钮时启动定时器。
4. 安全处理stopTimer方法
给timer增加初始化判断,避免未初始化时调用cancel():
fun stopTimer (){ if(::timer.isInitialized){ timer.cancel() } }
修改后的完整代码
MainActivityViewModel.kt
package com.shahid.timerlivedata import android.os.CountDownTimer import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel class MainActivityViewModel : ViewModel() { private lateinit var timer : CountDownTimer private val remaining_seconds = MutableLiveData<Int>() private val _finished = MutableLiveData<Boolean>() val finished : LiveData<Boolean> get() = _finished val seconds : LiveData<Int> get() = remaining_seconds var timerValue = MutableLiveData<Long>(0L) // 设置默认初始值 fun startTimer(){ val targetTime = timerValue.value ?: return // 先取消已有定时器,避免重复创建 if(::timer.isInitialized){ timer.cancel() } timer = object : CountDownTimer(targetTime,1000) { override fun onTick(millisUntilFinished: Long) { val remainingTime = millisUntilFinished/1000 remaining_seconds.value = remainingTime.toInt() } override fun onFinish() { _finished.value = true } }.start() // 启动定时器 } fun stopTimer (){ if(::timer.isInitialized){ timer.cancel() } } override fun onCleared() { super.onCleared() // ViewModel销毁时取消定时器,避免内存泄漏 stopTimer() } }
MainActivity.kt
package com.shahid.timerlivedata import android.annotation.SuppressLint import android.os.Bundle import android.widget.Button import android.widget.EditText import android.widget.TextView import android.widget.Toast import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider class MainActivity : AppCompatActivity() { @SuppressLint("MissingInflatedId") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContentView(R.layout.activity_main) ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets } val txtCount = findViewById<TextView>(R.id.txtCount) val edtCount = findViewById<EditText>(R.id.edtCount) val btnStart = findViewById<Button>(R.id.btnStart) val btnStop = findViewById<Button>(R.id.btnStop) val mAVieModel = ViewModelProvider(this).get(MainActivityViewModel::class.java) mAVieModel.seconds.observe(this) { seconds -> txtCount.text = "$seconds" } mAVieModel.finished.observe(this) { check -> if(check){ Toast.makeText(this,"Time Completed!!", Toast.LENGTH_LONG).show() } } btnStart.setOnClickListener{ val input = edtCount.text.toString() if(input.isEmpty()){ Toast.makeText(this,"Please Fill the Time", Toast.LENGTH_LONG).show() }else{ try { val seconds = input.toLong() mAVieModel.timerValue.value = seconds * 1000 mAVieModel.startTimer() } catch (e: NumberFormatException) { Toast.makeText(this,"Please enter a valid number", Toast.LENGTH_LONG).show() } } } btnStop.setOnClickListener{ mAVieModel.stopTimer() } } }
额外优化:增加了输入格式异常捕获,避免用户输入非数字时崩溃;修正了控件类型强转错误(原代码把Button转成TextView)
内容的提问来源于stack exchange,提问作者Shahid Faried
相关产品推荐
相关产品推荐

