与onSensorChanged方法问题 [英] problems with onSensorChanged method

查看:343
本文介绍了与onSensorChanged方法问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是这样的:

我需要手机创建一个MediaPlayer对象就是被称为SMP时:

它看到它已经动摇,这证实了SMP尚未创建,它会看到一个按钮叫剑柄器isChecked。

当满足这些条件下,它应该创建播放器,播放声音,并破坏播放器。我的问题是,它会播放声音最多两次,然后强制关闭。我不知道为什么,当我用它在应用程序按钮我的逻辑不工作,它会导致任何问题。

我的想法是,我已经得到了太多的MediaPlayer未使用的对象,但我不能看到,因为OnCompletionListener应调用release()每次播放完毕。

不过,我敢肯定它不会那么难比我的好主意。这里的code。感谢您的帮助。

更新:这是code现在我正在运行:

 公共无效onSensorChanged(INT传感器,浮动[]值){    同步(锁){
        如果(!jobRunning){
            jobRunning = TRUE;
        }其他{
            返回;
        }
    }    如果(传感器== SensorManager.SENSOR_ACCELEROMETER){
        长CURTIME = System.currentTimeMillis的();        如果((CURTIME - LASTUPDATE)→100){
            长diffTime =(CURTIME - LASTUPDATE);
            LASTUPDATE = CURTIME;            X =值[SensorManager.DATA_X]
            Y =值[SensorManager.DATA_Y]
            Z =值[SensorManager.DATA_Z]            浮速度= Math.abs(X + Y + Z - last_x - last_y - last_z)
                              / * diffTime 10000;
                如果(速度> SHAKE_THRESHOLD和放大器;&安培; hilt.isChecked()==真){                    SMP = MediaPlayer.create(getBaseContext(),swingSoundFx [rnd.nextInt(SWING_NUM)]);
                    smp.setOnCompletionListener(新OnCompletionListener(){                        @覆盖
                        公共无效onCompletion(MediaPlayer的MP){
                            如果(SMP!= NULL){
                                smp.stop();
                                smp.release();
                            }                        }
                    }                    );
                    smp.start();
        }
        last_x = X;
        last_y = Y;
        last_z = Z;
        }
    }
    同步(锁){
        jobRunning = FALSE;
    }
}


解决方案

原始anwser

您有一个实例变量SMP。每当你开始要覆盖那个变量声音,但你的听众会尝试关闭的目前的存储播放器,因此,如果由于某种原因你更换了SMP变量其间,你就会有一个挥之不去的球员,你从未关闭(和缓存效果将会使即使丑陋,如果 SMP 不挥发,但它不工作两种方式)。该修复程序是简单:不要关最后的球员存储,让previous而没有得到封闭一滴,只需用MediaPlayer的传递给你的onCompletion的参数

 公共无效onCompletion(MediaPlayer的MP){
  mp.stop();
  mp.release();

这是这里最明显的问题。

现在,将不足以解决您的问题,因为尽管这将有助于正确清理你的球员,你仍然会启动几个人在同一时间。我不认为在播放器上调用start()不会导致IsPlaying模块()立即为真 - 它可能等待的媒体是开放的第一和所有,这给出了一个时间窗口的code运行另一个一(更是如此,因为扮演很可能会在另一个线程发生)。碰巧的是,没准你会马上:任何强劲的加速,您将收到高值多次连续,所以你可能会连续输入您试了几次,经常20倍第二个或更多,如果你要求实现快速更新。在这短短的时间你的手机会不会有时间来打开媒体文件,并开始播放它,所以IsPlaying模块()仍然会返回false,你会催生一个新的。

所以,其实我的建议是,而不是依靠IsPlaying模块()知道的东西是否在作品中,有一个同步布尔独立于媒体播放器,它告诉你,如果你确实是等待玩启动或播放。把它转化为真正启动时,假你停下来,摧毁了你们的球员,在回调,没有忘记同步后。这应该工作。结果
修改:很抱歉,如果这是不明确的,你应该同步访问你的布尔但不能在布尔本身或任何为此事临时对象。例如,当声音开始播放

 同步(锁){
  如果(soundRunning)回报;
  soundRunning = TRUE;
}

和停止时,在处理程序:

 同步(锁){
  soundRunning = FALSE;
}

进一步走出去,如果你希望播放声音好几次,我建议有一个较长的寿命媒体播放器和重置,而不是创建和销毁每次之一。这应该节省一些资源。当然,如果你这样做,不要忘记去破坏它,当你知道你不会播放声音了。

答新code修改

好吧,是有原因的此不起作用两项。结果
第一个是,你没有改变onCompletion处理程序停止合适的对象。你还在试图停止播放器,它没有任何更多点上正在播放的一个,因为您已改写变量。你可以重新读取初始anwser第一段,因为它仍持有。

另一个原因是,你的布尔是不是preventing两个声音同时播放,但preventing在同一时间的两倍运行的方法。这不是做正确的事。此外,它不仅做错误的事情,它甚至没有做它以正确的方式。在详细信息:

这不是prevent以正确的方式从在同一时间运行两次方法。这就是同步工作。你可以让你的方法进行同步,任何后续调用将等待所有正在运行的实例来完成。你不会需要一个布尔变量来做到这一点。你需要一个变量,因为:结果
您想避免在开始播放声音,当一个已经运行,无法避免同时运行启动声音的方法。和声音播放远远超过你的方法,这就是为什么你需要将其存储在一个变量的运行寿命。因此,你应该把布尔为true,当你开始玩,回false;如果停止播放,像这样的:

 如果(速度> SHAKE_THRESHOLD和放大器;&安培; hilt.isChecked()==真){
  同步(锁){
    如果(IsPlaying模块)的回报;
    IsPlaying模块= TRUE;
  }
  SMP = MediaPlayer.create(getBaseContext(),swingSoundFx [rnd.nextInt(SWING_NUM)]);
  smp.setOnCompletionListener(新OnCompletionListener(){
    @覆盖
    公共无效onCompletion(MediaPlayer的MP){
      mp.stop();
      mp.release();
      同步(锁定){IsPlaying模块= FALSE; }
    }
  });
  smp.start();
}

我没有刻意去测试这个所以它可能有缺陷,但它的大方向。你不关心方法是否被同时多次调用(或者,如果你这样做,你可以分别进行同步),但你要知道,当玩家玩到就跳过运行另一个在同一时间,这是什么布尔的解释是:

希望帮助,并因为它比较清楚,我的初步答案。

my issue is this:

i need the phone to create a MediaPlayer object called smp when:

it sees that it's been shaken, it confirms that smp isn't already created, it sees that a button called hilt isChecked.

when it satisfies these conditions, it should create the player, play a sound, and destroy the player. the problem i have is that it will play the sound at most twice and then force close. i'm not sure why my logic doesn't work when i've used it for buttons in the app and it causes no problems.

my thinking is that i've got too many unused MediaPlayer objects but i can't see how since the OnCompletionListener should be calling release() every time the player is finished.

anyway, i'm sure it won't be that hard to better minds than mine. here's the code. thanks for any help.

UPDATE: this is the code i'm running now:

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;
    }
}

解决方案

Original anwser

You have smp in an instance variable. Whenever you start a sound you are overwriting that variable, but your listener tries to close the currently stored player, so if for some reason you replaced the smp variable inbetween, you'll have a lingering player that you never closed (and caching effects will make that even uglier if smp is not volatile, but it doesn't work either way). The fix to that is simple: instead of closing the last player stored and let the previous one drop without ever getting closed, just use the MediaPlayer passed to you in the argument of onCompletion.

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

This is the most obvious problem here.

Now that won't be enough to solve your problem because though that will help cleanup your players correctly, you will still start several of them at the same time. I don't think that calling start() on the player does result in isPlaying() being true immediately - it probably waits for the media to be open first and all, which gives a time window for your code to run another one (even more so since playing will likely happen in another thread). As it happens, chances are you will right away : on any strong acceleration you will receive high values several times in a row, so you will probably enter your test several times in a row, as often as 20 times a second or more if you asked for fast updates. In that short interval your phone won't have had time to open the media file and start playing it, so isPlaying() will still return false and you'll spawn a new one.

So actually what I would suggest is, instead of relying on isPlaying() to know whether something is in the works, having a synchronized boolean separate from the media player which tells you if you are indeed waiting for playing to start or playing. Turn it to true when start, to false after you stopped and destroyed your player, in the callback, without forgetting synchronization. That should work.
Edit: Sorry if that wasn't clear, you should synchronize the accesses to your boolean but not on the boolean itself or any transient object for that matter. For example, when the sound begins playing:

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

And when it stops, in the handler:

synchronized(lock) {
  soundRunning = false;
}

Going further, if you expect to play sounds several times, I would suggest having a longer-lived media player and reset it instead of creating and destroying one every time. This should conserve some resources. Of course if you do this don't forget to destroy it when you know you won't play sounds any more.

Answer for new code edit

Okay, there are two reasons for which this does not work.
The first one is, you did not change the onCompletion handler to stop the right object. You are still trying to stop a Player that does not point any more on the one that is playing, because you overwrote the variable. You can re-read the first paragraph of the initial anwser, as it still holds.

The other reason is, your boolean is not preventing two sounds to play at the same time, but preventing the method to run twice at the same time. It's not doing the right thing. Besides not only does it do the wrong thing, it's not even doing it in the right way. In the detail:

It's not the right way to prevent a method from running twice at the same time. That's the job of synchronize. You can just make your method synchronized, and any subsequent invocation will wait for any running instance to finish. You would not need a boolean variable to do that. You need a variable because:
You want to avoid starting playing a sound when one is running already, not avoid running concurrently the method that starts the sound. And the sound playing far exceeds the running life of your method, which is why you need to store it in a variable. As such, you should turn the boolean to true when you start playing and back to false when you stop playing, like this:

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();    
}

I didn't bother to test this so it might have bugs, but it's the general direction. You don't care whether the method gets called several times concurrently (or if you do, you can just synchronize it separately), but you want to know when the player is playing to just skip running another one at the same time, and that's what the boolean is supposed to mean.

Hope that helps, and that it was clearer that my initial answer.

这篇关于与onSensorChanged方法问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆