Trying to implement button click sound with MediaPlayer crashes - android

I have a custom view (a button) and I want it to have a certain sound when pressed.
private void playSound() {
if (mediaPlayer == null)
initMediaPlayer();
try {
if (mediaPlayer.isPlaying())
mediaPlayer.stop();
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
private void initMediaPlayer() {
try {
AssetFileDescriptor afd = getContext().getAssets().openFd("music/number_tap.m4a");
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
} catch (IOException e) {
e.printStackTrace();
}
}
I call the playSound method every time the button is clicked. It works well as long as there is only one button on screen. No problem at all.
It crashes if I have more buttons on the screen and I press the first button a couple of times (works) and then some other button 2 times (crashes on the second press of the second button).
Any idea what could be wrong? Here's the crash stacktrace.
FATAL EXCEPTION: main
Process: com.matejhacin.multiflow, PID: 20991
java.lang.IllegalStateException
at android.media.MediaPlayer._prepare(Native Method)
at android.media.MediaPlayer.prepare(MediaPlayer.java:1163)
at com.matejhacin.multiflow.views.KeyButtonView.playSound(KeyButtonView.java:126)
at com.matejhacin.multiflow.views.KeyButtonView.onTouch(KeyButtonView.java:77)

From what I can see, you have your own implementation of that button KeyButtonView.java, and in that class you have mediaPlayer as a member variable, so when you have two buttons, respectively you have two instances of KeyButtonView class and two instances of mediaPlayer. Why don't you use one instance of mediaPlayer, let's say you make it static, and make playSound() and initMediaPlayer() methods synchronized and static, this should be a quick and simple change, just to test if there is the issue. And something else, you use onTouch() to handle a click, why don't you try with OnClickListener? If you do not use onTouch properly you will end with bunch of calls on the UI thread, and each blocking at prepare(), this is not good. Once you fix this issue, you should consider rewrite it with prepareAsync() and setOnPreparedListener().

Related

Audio file in application doesn't stop on click, it starts playing again

So I put an audio file in my application and it's supposted play when I touch the button and stop when I touch it again.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button one = (Button) findViewById(R.id.buttonId);
final MediaPlayer mp = new MediaPlayer();
one.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
if(mp.isPlaying())
{
mp.stop();
}
try {
mp.reset();
AssetFileDescriptor afd;
afd = getAssets().openFd("mosq.mp3");
mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
mp.prepare();
mp.setLooping(true);
mp.start();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
here is my code, this part:
if(mp.isPlaying())
{
mp.stop();
}
didn't work for some reason.
Make sure you put a return statement below mp.stop().
From what I can understand the sound does stop but then it starts again because the next part of the code still gets executed
As George D correctly pointed out, you start the media playing unconditionally, even if you just stopped it. You could use his solution or do something like:
if(mp.isPlaying())
{
mp.stop();
}
else {
try {
mp.reset();
AssetFileDescriptor afd;
afd = getAssets().openFd("mosq.mp3");
mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
mp.prepare();
mp.setLooping(true);
mp.start();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
This has several other potential bugs in it:
* I'm not sure if this is what you intended but the player won't ever pause, just stop and restart from the beginning. If you try to resume or play again it'll completely reload the audio file every time. At a minimum this is a waste of resources, plus it's likely not the expected behavior from a UI perspective.
* You don't want to define the MediaPlayer object as a local variable within the OnCreate method. The only reason this works at all is you have a memory leak (you never unsubscribe your event handler for the click); if you didn't have the memory leak the object would become eligible for garbage collection as soon as you completed the onCreate method and, as far as the framework was concerned, it would no longer exist.

Media Player Class in Android the implementation scenario

for last three weeks I have worked on a Media Player in Android.I am trying to find a solution of how can I make my Media Player to change the song when it's already playing one.
Here is my Listener on the RecyclerView
musicList.addOnItemTouchListener(
new RecyclerItemClickListener(getApplicationContext(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, final int position) {
currentPosition = position;
if(!mediaPlayer.isPlaying()){
musicThread.start();
} else {
mediaPlayer.reset();
}
}
})
);
}
and my Thread is this:
final Thread musicThread = new Thread(new Runnable(){
#Override
public void run() {
try {
URL = getMusicURL(myDataset[currentPosition]);
try {
mediaPlayer.setDataSource(URL);
//mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.prepareAsync(); // prepare async to not block main thread
} catch (IOException e) {
e.printStackTrace();
Log.i("TEST","Eroare: "+e.getMessage());
}
} catch (StorageApiException e) {
e.printStackTrace();
Log.i("TEST","Eroare: "+e.getMessage());
}
}
});
I think you have a mess. First of all, you dont need a thread to play music, the own mediaplayer API does it for you when you call mediaPlayer.start(). However, you have to care about the time it takes to prepare the data source if you are for example streaming online music. For this, just use mediaPlayer.prepareAsync() and register a callback. When it has finished preparing, you can automatically start playing or do whatever you want.
If you want to change the data source, just follow the automaton map that you can find in MediaPlayer docs. Essentially, when the user selects another track, you register the call in your button listener, then reset the mediaPlayer, and recall all prepare, start... cycle again. By the way, it is advised to deploy all your mediaplayer code into a service so that it can keep playing even though the user has closed your activity.

Sound comes from the app when it is paused

I have problems with my app because multimedia sound is heard when the app is in background
I have defined my Media player like this;
private void playLocalAudio(int R1)throws Exception
{
MediaPlayer mediaPlayer = MediaPlayer.create(this,R1);
mediaPlayer.start();
}
For calling PlayLocalAudio I do:
try{
playLocalAudio(R.raw.fartw1);
}
catch (Exception e) {
e.printStackTrace();
}
}});
But I am not able to call correctly MediaPlayer.Stop()
I am trying:
public void onPause()
{
super.onPause();
mediaplayer.stop();
}
But it doesn't work. Could you help me?
I'm guessing that your code has a class variable mediaPlayer that's not visible in your example. In that case you have variable shadowing, because you're instantiating a new mediaPlayer in playLocalAudio and that instance is not visible inside the pause method. So stop is never called. Remove the MediaPlayer class name from the declaration in playLocalAudio.

Android MediaPlayer OnCompletionListener not called in JUnit Testcase (AndroidTestCase)!

I am using the Android MediaPlayer class and trying to write a testcase which verifies that the onCompletion method is called.
I use it to play the next track after the previous one is finished.
When I run the app using the emulator (2.1 or 4.0) the onCompletion method is called and the next track starts playing, but in the testcase it is not.
Here is the simplified code:
public class MediaPlayerControllerTest extends AndroidTestCase implements OnCompletionListener {
public void testContinuePlayNextTrack() {
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setOnCompletionListener(this);
try {
mediaPlayer.setDataSource("/mnt/sdcard/5749/01.mp3");
mediaPlayer.prepare();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mediaPlayer.start();
}
#Override
public void onCompletion(MediaPlayer mp) {
System.out.println("ON COMPLETION!!!!!");
}
}
The code above is normally part of a class MediPlayerController which is used in the app and the test, so it's the same class. I broke the problem down to the few lines of code above.
This cost me a lot of hours. I hope someone has a solution!
Thanks a lot!!!
I have encountered this problem, and nothing was working for me. From the official Site (click me) for the MediaPlayer one can find the following sentence:
In order to receive the respective callback associated with these listeners, applications are required to create MediaPlayer objects on a thread with its own Looper running (main UI thread by default has a Looper running).
I was a bit lost, as there was no hint for this need on in the API for onCompletionListener himself.
I think the problem is that your test case is no longer running when the MediaPlayer finishes playing the mp3. So your solution is to keep the test case alive until the onCompletion() is fired.
I had a similar issue when playing a file from an IntentService. The Service was being destroyed before the callback was initiated.
Actually, the reason is that the MediaPlayer is a local variable. After the testContinuePlayNextTrack() is finished, the MediaPlayer is collected by GC. So the fix is easy, make your MediaPlayer a member of the class.
public class MediaPlayerControllerTest extends AndroidTestCase implements OnCompletionListener {
MediaPlayer mediaPlayer = new MediaPlayer();
public void testContinuePlayNextTrack() {
mediaPlayer.setOnCompletionListener(this);
try {
mediaPlayer.setDataSource("/mnt/sdcard/5749/01.mp3");
mediaPlayer.prepare();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mediaPlayer.start();
}
#Override
public void onCompletion(MediaPlayer mp) {
System.out.println("ON COMPLETION!!!!!");
}
}

Android MediaPlayer - only one instance at any given time?

I have a audio player app, where there is a Main activity that shows 3 audio sample urls. On click on one, it goes to a Details Activity, which has a play and pause button, to start and pause the audio.
My problem is that, when I start the Main activity, and say click on audio 1, I hit play on Details activity. This starts the MediaPlayer and the audio starts to play. When I go back to the Main activity, the audio is still playing, which is what I want. Now, when I click on audio 1 again, and go to Details Activity and hit play again, there seems to be a new MediaPlayer starting the audio. So I have 2 tracks playing together!
Is there a way I can have only one MediaPlayer instance at any given time?
Thanks
Chris
You should consider the Singleton pattern. Make a class MyPlayer that has a static method getMediaPlayer() that returns the same instance of MediaPlayer each time called.
Singleton Class
public final class MySingleton extends Application {
static MediaPlayer instance;
public static MediaPlayer getInstance() {
if (instance == null)
{
instance = new MediaPlayer();
}
return instance;
}
}
Adapter Where your List
Initialize Your Singleton Class One time in Constructor
private static MediaPlayer mMediaPlayer = MySingleton.getInstance();
if (mMediaPlayer.isPlaying()) {
try {
mMediaPlayer.reset();
mMediaPlayer.stop();
mMediaPlayer.setDataSource(mainActivity, Uri.parse(songsarraylist.get(position).getPath()));
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (Exception e) {
Toast.makeText(mainActivity, "Catching", Toast.LENGTH_SHORT).show();
}
} else {
try {
mMediaPlayer.setDataSource(mainActivity, Uri.parse(songsarraylist.get(position).getPath()));
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
}

Categories

Resources