I’m new to Android development. I’m building a simple Android application which analyzes accelerometer sensor data and plays a sound with MediaPlayer. Code for SensorEventListener implementation:
class AccelListener implements SensorEventListener {
boolean firstUpdate = true;
volatile boolean stopped = true;
private MediaPlayer mplayer;
//some variables to store calculations
public AccelListener(MediaPlayer mediaplayer) {
mplayer = mediaplayer;
mplayer.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
if (mplayer == mp) {
synchronized (AccelListener.this) {
stopped = true;
}
Log.d("MediaPlayer Callback", "stopped=" + stopped);
}
}
});
}
public void onSensorChanged(SensorEvent event) {
updateAccel(event.values);
if (isAccelChanged()) {
Log.d("Accel Listener", "stopped_before=" + stopped);
synchronized (this) {
if (stopped) {
stopped = false;
firstUpdate = true;
mplayer.start();
Log.d("Accel Listener", "stopped_after=" + stopped);
}
}
}
}
}
The problem is that when I launch the application and start shaking the phone, the sound plays several times at the same moment. Audio file is about 3-4 seconds long, so there’s no way it plays to the end and then reacts to the next shake.
Logcat shows the following:
08-12 22:16:24.693: D/Accel Listener(5382): stopped_before=true
08-12 22:16:24.693: D/Accel Listener(5382): stopped_after=false
08-12 22:16:24.693: D/Accel Listener(5382): stopped_before=true
08-12 22:16:24.703: D/Accel Listener(5382): stopped_after=false
08-12 22:16:28.777: D/MediaPlayer Callback(5382): stopped=true
08-12 22:16:28.777: D/MediaPlayer Callback(5382): stopped=true
Critical sections in the onSensorChanged() and onCompletion() are locked on the same object (I make only one instance of AccelListener in the program), so there should be no parallel access to stopped.
Furthermore, this variable is declared as volatile, so anyone entering a critical should have a fresh, up-to-date value. But it seems like two different threads actually enter the critical section simultanously, both having the same flag value. How could this happen, and where am I wrong?
P.S. The guy over here seems to have the same problem, but none of the solutions for his case (like synchronizing on separate object or using the same instance of MediaPlayer) worked for me.
Problem solved. I didn’t unregister
AccelListener, and it seems like debugger don’t properly stop running version of the program. Each consecutiveRuncommand just started main activity, it instantiatedAccelListener, registered it, and so on. Adding the following code fixed the whole thing: