Android Monkey跑出来的一个Bug:IllegalStateException

本文探讨了Android录音应用中遇到的一个Bug,即在调用stopPlay()函数时,Timer任务异常终止的问题。通过分析代码,发现刷新频率设置过低导致的异常。提出了解决方案,包括增加刷新时间间隔或使用锁机制来避免此类问题。

今天Monkey跑出来的一个Bug:

E/AndroidRuntime( 3359): FATAL EXCEPTION: Timer-0

E/AndroidRuntime( 3359): Process: com.android.soundrecorder, PID: 3359
E/AndroidRuntime( 3359): java.lang.IllegalStateException
E/AndroidRuntime( 3359):  at android.media.MediaPlayer.isPlaying(Native Method)

E/AndroidRuntime( 3359):  at com.android.soundrecorder.SoundRecorder$MyTimerTask.run(SoundRecorder.java:1700)


调用stopPlay()函数时出错了,原因是10毫秒刷新太快了,即便stopPlay()中作了mTimerTask.cancel();mTimerTask = null;操作,还是走到了代码第9行。

解决办法:刷新时间调大点(300ms,500ms)或者加锁吧。

代码片段:

    

Timer timer = new Timer();
        if (null != mTimerTask) {
            mTimerTask.cancel();
            mTimerTask = null;
        }
        mTimerTask = new MyTimerTask();
        timer.schedule(mTimerTask, 0, 10);

class MyTimerTask extends TimerTask {
        @Override
        public void run() {
            if (isChanging == true) {
                return;
            }
            if (null != mTimerTask) {
                if (null != mPlayer) {
                    if (mPlayer.isPlaying()) {
                        mSeekBarLeftTime = mPlayer.getCurrentPosition() / 1000;
                        mHandler.sendEmptyMessage(LEFT_TIME);
                    }
                }
            }
        }
    }
public void stopPlay() {
        LogUtil.v(TAG, "stopplay()");
        if (null != mTimerTask) {
            mTimerTask.cancel();
            mTimerTask = null;
        }
        if (mPlayer == null) 
            return;
        mPlayer.stop();
        mPlayer.release();
        mPlayer = null;
    }

<think>我们遇到了一个错误:`IllegalStateException: This stopwatch is already stopped.` 这个错误通常发生在使用类似Spring的StopWatch工具时,当我们尝试停止一个已经停止的计时器时,就会抛出这个异常。 原因分析: StopWatch通常有开始和停止两个状态。当我们调用`stop()`方法时,计时器会停止并记录结束时间。 如果再次调用`stop()`方法,而计时器已经处于停止状态,那么就会抛出`IllegalStateException`。 解决方案: 1. 在调用`stop()`方法之前,确保计时器是处于运行状态(即已经调用了`start()`且没有停止)。 2. 避免重复停止同一个计时器。 3. 如果需要在多个地方停止计时器,可以使用条件判断来确保只停止一次。 示例代码(使用Spring的StopWatch): 假设我们有一个方法,其中使用了StopWatch,但可能由于某种原因(如条件分支)导致重复停止。 错误示例: ```java StopWatch stopWatch = new StopWatch(); stopWatch.start("task1"); // 执行一些操作... stopWatch.stop(); // 可能由于某些条件,再次调用了stop if (someCondition) { stopWatch.stop(); // 这里会抛出IllegalStateException } ``` 正确做法:确保每个start对应一个stop,并且只stop一次。 我们可以通过检查StopWatch的状态来避免重复停止。但是,Spring的StopWatch并没有直接提供状态检查的方法。 因此,我们需要自己管理状态,或者使用try-finally确保只停止一次。 另一种方法是使用`if (stopWatch.isRunning())`来判断(注意:Spring StopWatch从5.2.2.RELEASE版本开始提供了`isRunning()`方法)。 示例代码(使用条件判断): ```java import org.springframework.util.StopWatch; public class Example { public static void main(String[] args) { StopWatch stopWatch = new StopWatch(); stopWatch.start("task1"); try { // 执行任务... } finally { // 确保只停止一次 if (stopWatch.isRunning()) { stopWatch.stop(); } } } } ``` 如果使用的是旧版本的Spring(没有isRunning方法),我们可以自己维护一个状态变量: ```java import org.springframework.util.StopWatch; public class Example { public static void main(String[] args) { StopWatch stopWatch = new StopWatch(); boolean isRunning = false; try { stopWatch.start("task1"); isRunning = true; // 执行任务... } finally { if (isRunning) { stopWatch.stop(); } } } } ``` 另外,如果是在循环中或者多个地方可能调用stop,需要确保逻辑正确。 还有一种情况:可能我们在多个分支中调用了stop,导致重复停止。这时候需要重构代码,确保每个任务只停止一次。 例如,将stop放在finally块中,并且只停止一次。 总结:避免重复调用stop()方法,确保每个start()之后只调用一次stop()。 如果问题发生在你自己的代码中,请检查是否有重复调用stop()的情况,并按照上述方法进行修正。 如果你使用的是其他类似的计时器工具,原理相同。 下面是一个使用Spring StopWatch的完整示例,并确保不会重复停止: ```java import org.springframework.util.StopWatch; public class StopWatchExample { public static void main(String[] args) { StopWatch stopWatch = new StopWatch("My StopWatch"); stopWatch.start("task1"); try { // 模拟任务执行 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { // 检查是否在运行,然后停止 if (stopWatch.isRunning()) { stopWatch.stop(); } } System.out.println(stopWatch.prettyPrint()); } } ``` 注意:上述代码需要Spring框架的StopWatch类,确保你的项目中包含了Spring-core依赖。 如果你没有使用Spring,而是使用其他库或自定义的StopWatch,请根据具体实现调整。 如果问题仍然存在,请检查代码中是否有多个线程同时操作同一个StopWatch实例,这可能会导致并发问题。在多线程环境下,每个线程应该使用自己的StopWatch实例。 相关问题: §§相关问题§§: 1. 如何避免在多个分支中重复调用StopWatch的stop方法? 2. 除了使用StopWatch,还有哪些Java计时工具可以使用? 3. 如何在多线程环境下正确使用StopWatch? 4. Spring StopWatch和Apache Commons的StopWatch有什么区别?</think>### 解决 `IllegalStateException: This stopwatch is already stopped.` 错误 这个错误通常发生在使用计时工具(如 Spring 的 `StopWatch` 或 Apache Commons 的 `StopWatch`)时,重复调用 `stop()` 方法导致的。以下是解决方案和示例代码: --- ### 解决方案 1. **确保每个 `start()` 只对应一个 `stop()`** 2. **在 `stop()` 前检查计时器状态** 3. **使用 `try-finally` 确保资源释放** 4. **避免在分支逻辑中重复调用 `stop()`** --- ### 示例代码(使用 Spring `StopWatch`) ```java import org.springframework.util.StopWatch; public class StopWatchExample { public static void main(String[] args) { StopWatch stopWatch = new StopWatch("TaskTimer"); try { stopWatch.start("task1"); // 执行任务1 Thread.sleep(500); // 模拟耗时操作 } finally { // 关键:检查状态后再停止 if (stopWatch.isRunning()) { stopWatch.stop(); } } System.out.println(stopWatch.prettyPrint()); } } ``` ### 代码解释 1. **`stopWatch.isRunning()`** 检查计时器是否正在运行,避免重复停止。 2. **`try-finally` 块** 确保无论是否发生异常,都会尝试停止计时器。 3. **状态检查** 只在计时器运行时调用 `stop()`,防止重复触发异常。 --- ### 错误场景复现与修复 **错误代码**(会抛出异常): ```java StopWatch stopWatch = new StopWatch(); stopWatch.start(); stopWatch.stop(); // 第一次停止 stopWatch.stop(); // 第二次停止!抛出 IllegalStateException ``` **修复后代码**: ```java StopWatch stopWatch = new StopWatch(); stopWatch.start(); // ... 业务逻辑 ... if (stopWatch.isRunning()) { // 安全防护 stopWatch.stop(); } ``` --- ### 其他常见计时工具的正确用法 #### 1. Apache Commons `StopWatch` ```java import org.apache.commons.lang3.time.StopWatch; public class CommonsStopWatchExample { public static void main(String[] args) { StopWatch stopWatch = StopWatch.createStarted(); // 自动开始 try { Thread.sleep(300); } finally { if (stopWatch.isStarted() && !stopWatch.isStopped()) { stopWatch.stop(); // 安全停止 } } System.out.println("Time: " + stopWatch.getTime() + "ms"); } } ``` #### 2. Java `System.nanoTime()`(手动实现) ```java public class ManualTimer { private long startTime; private boolean isRunning = false; public void start() { if (isRunning) throw new IllegalStateException("Timer already running"); startTime = System.nanoTime(); isRunning = true; } public long stop() { if (!isRunning) throw new IllegalStateException("Timer not running"); isRunning = false; return System.nanoTime() - startTime; } } ``` --- ### 关键注意事项 1. **状态管理** 始终通过 `isRunning()` 或自定义标志位跟踪计时器状态。 2. **单次停止原则** 每个 `start()` 必须对应**最多一次** `stop()`。 3. **线程安全** 在多线程环境中,使用局部变量而非共享实例。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值