onSensorChanged方法的问题

时间:2010-10-22 03:33:05

标签: android media-player

我的问题是:

我需要手机在以下时间创建名为smp的MediaPlayer对象:

它看到它被动摇了,它确认smp尚未创建,它看到一个名为hilt的按钮被检查。

当它满足这些条件时,它应该创建播放器,播放声音并摧毁播放器。我的问题是,它最多会播放两次声音,然后强行关闭。我不确定为什么我的逻辑在我用于应用程序中的按钮时不起作用而且没有问题。

我的想法是我有太多未使用的MediaPlayer对象,但我无法看到每次播放器完成后OnCompletionListener应该调用release()。

无论如何,我敢肯定,比我的更好的思想不会那么难。这是代码。谢谢你的帮助。

更新:这是我现在正在运行的代码:

public void onSensorChanged(int sensor, float[] values) {

    synchronized (lock){
        if (!jobRunning){
            jobRunning = true;
        }else{ 
            return;
        }
    }

    if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
        long curTime = System.currentTimeMillis();

        if ((curTime - lastUpdate) > 100) {
            long diffTime = (curTime - lastUpdate);
            lastUpdate = curTime;

            x = values[SensorManager.DATA_X];
            y = values[SensorManager.DATA_Y];
            z = values[SensorManager.DATA_Z];

            float speed = Math.abs(x+y+z - last_x - last_y - last_z)
                              / diffTime * 10000;


                if (speed > SHAKE_THRESHOLD && hilt.isChecked() == true) {

                    smp = MediaPlayer.create(getBaseContext(), swingSoundFx[rnd.nextInt(SWING_NUM)]); 
                    smp.setOnCompletionListener( new OnCompletionListener(){

                        @Override
                        public void onCompletion(MediaPlayer mp) {
                            if (smp != null){
                                smp.stop();
                                smp.release();
                            }

                        }   
                    }

                    );
                    smp.start();    
        }
        last_x = x;
        last_y = y;
        last_z = z;


        }
    }
    synchronized (lock){
        jobRunning = false;
    }
}

1 个答案:

答案 0 :(得分:3)

Original anwser

你有一个实例变量的smp。每当你开始声音时你都会覆盖那个变量,但是你的听众试图关闭当前存储的播放器,所以如果由于某种原因你在中间替换了smp变量,那么你将会有一个挥之不去的玩家永远不会关闭(如果smp不易变,缓存效果会变得更加丑陋,但它无论如何都不起作用)。解决这个问题的方法很简单:不是关闭最后一个存储的玩家而是让前一个玩家没有关闭,只需使用在onCompletion参数中传递给你的MediaPlayer。

public void onCompletion(MediaPlayer mp) {
  mp.stop();
  mp.release();

这是最明显的问题。

现在这还不足以解决您的问题,因为虽然这有助于正确清理您的玩家,但您仍然可以同时启动其中的几个。我不认为在播放器上调用start()确实会导致isPlaying()立即生效 - 它可能会等待媒体首先打开并且全部打开,这为您的代码运行另一个媒体提供了时间窗口(甚至更多,因为播放可能会发生在另一个线程)。碰巧的是,你很可能会立即:在任何强烈的加速度下,你会连续几次获得高值,所以你可能会连续几次输入你的测试,如果你经常连续20次或更多次要求快速更新。在这么短的时间间隔内,您的手机将没有时间打开媒体文件并开始播放,因此isPlaying()仍将返回false,您将生成一个新的。

所以实际上我建议的是,而不是依赖于isPlaying()来知道某些东西是否在起作用,将 synchronized 布尔与媒体播放器分开,告诉你你是否确实等待比赛开始或比赛。在开始时将其变为true,在停止并摧毁播放器后在回调中将其设置为false,而不会忘记同步。这应该工作。
编辑:很抱歉,如果不清楚,您应该同步对布尔值的访问,而不是布尔本身或任何瞬态对象的访问。例如,当声音开始播放时:

synchronized(lock) {
  if (soundRunning) return;
  soundRunning = true;
}

当它停止时,在处理程序中:

synchronized(lock) {
  soundRunning = false;
}

更进一步,如果您希望多次播放声音,我建议您使用寿命更长的媒体播放器并重置它,而不是每次创建和销毁一个。这应该节省一些资源。当然,如果你这样做,当你知道不再播放声音时,不要忘记销毁它。

回答新代码编辑

好的,有两个原因可以解决这个问题 第一个是,您没有更改onCompletion处理程序来停止正确的对象。你仍然试图阻止一个不再指向正在播放的播放器的播放器,因为你覆盖了变量。您可以重新阅读初始anwser的第一段,因为它仍然存在。

另一个原因是,您的布尔值不会阻止两个声音同时播放,但阻止该方法同时运行两次。这不是正确的事情。除了它不仅做错了,它甚至没有以正确的方式做到这一点。详细说明:

这不是防止方法同时运行两次的正确方法。这是synchronize的工作。您可以使方法同步,任何后续调用都将等待任何正在运行的实例完成。你不需要一个布尔变量来做到这一点。你需要一个变量,因为:
您希望避免在已经运行时开始播放声音,而不是避免同时运行启动声音的方法。并且声音播放远远超过了您的方法的运行寿命,这就是您需要将其存储在变量中的原因。因此,您应该在开始播放时将布尔值设置为true,并在停止播放时将其设置为false,如下所示:

if (speed > SHAKE_THRESHOLD && hilt.isChecked() == true) {
  synchronized(lock) {
    if (isPlaying) return;
    isPlaying = true;
  }
  smp = MediaPlayer.create(getBaseContext(), swingSoundFx[rnd.nextInt(SWING_NUM)]); 
  smp.setOnCompletionListener( new OnCompletionListener(){
    @Override
    public void onCompletion(MediaPlayer mp) {
      mp.stop();
      mp.release();
      synchronized(lock) { isPlaying = false; }
    }
  });   
  smp.start();    
}

我没有费心去测试它,所以它可能有错误,但这是大方向。您不关心该方法是否同时被多次调用(或者如果您这样做,您可以单独同步它),但是您想知道玩家何时正在玩,只是跳过同时运行另一个,这就是布尔值应该是什么意思。

希望有所帮助,而且我最初的答案更清楚。