I haave my own Custom Adapter Class called WordAdapter, and I am using a Media Player(named pronounce-global variable in the WordAdapter class). I have different activities in which each list item has a linear layout(named as linearLayout). I am setting onClickListener to it so that when the Linear Layout is clicked, a sound file is played. On completion of playing that sound, I want to free off any unwanted memory. But I do not know if I should use release() or reset(). I have checked previous questions asked on SO before, but I don't think it provides precise explanation for my situation so as to use which method.
NOTE: I should be able to play other audio files after this one too (After completing playing this audio file, when I click on other items in the same activity, I should be able to get the sound.)
linearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
pronounce = MediaPlayer.create(context, currentWord.getPronounceResourceID());
pronounce.start();
pronounce.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer player) {
//pronounce.release();
//pronounce.reset();
}
});
}
});
Do a reset before a release, but I suspect that only release is needed.
This might be easier to manage:
public void onClick(View view) {
if (pronounce != null) {
pronounce.reset();
pronounce.release();
pronounce = null;
}
pronounce = MediaPlayer.create(context, currentWord.getPronounceResourceID());
pronounce.start();
}
The reset method will simply stop any media and send the MediaPlayer instance back to the idle state. Exactly in the same state when it was created.
The release method destroys the media player and frees the majority of the unmanaged resources. When you call release, you should set the instance variable to null so that the remainder of the object is a candidate for garbage collection.
You might have some better performance if you just use reset and then reuse the existing mediaplayer instance on subsequent clicks.
Related
I have a RecyclerView with some video elements and they get restarted every time they get off-screen. I tried:
recyclerView.getRecycledViewPool().setMaxRecycledViews(RecyclerViewAdapter.TYPE_VIDEO, 0);
but no success. I also tried to do:
holder.setIsRecyclable(false)
inside my adapter, but videos still restart every time.
Is there any way to stop restarting videos, and somehow pause them and resume them once they are on screen again?
The videos are remote, not local. And I am using a class extending TextureView, not the Android's VideoView
Edit: I missunderstood the question. See original answer below.
I guess you have two possibilities:
Ugly, only possible if the list is not going to be too long™ and undermining the purpose of the recyclerview: use setItemViewCacheSize(int) to increase the number of viewholders that are cached to the number of videos you have and just pause and play during attach / detach.
much better: Store and restore the current playback timestamp of each video when the viewholder gets attached/dettached.
You can use an RecyclerView.OnChildAttachStateChangeListener that will be called whenever a view is attached / removed.
Do it like this:
mRecyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(View view) {
YourTextureView yourTextureView = (YourTextureView) view.findViewById(R.id.yourvideoviewid);
if (yourTextureView != null) {
// start your video
}
}
#Override
public void onChildViewDetachedFromWindow(View view) {
YourTextureView yourTextureView = (YourTextureView) view.findViewById(R.id.yourvideoviewid);
if (yourTextureView != null) {
// stop your video
}
}
});
Lets say I have several players I've created.
And Lets say that for each one of them, I want a different set on actions to be activated.
How can I locate which player has been finished (the implementation itself of the case sentence) in order to act differently for each player ?
For example: in order to handle a single player, all we have to do is to implement the onCompletion() method like this:
public void onCompletion(MediaPlayer mp){
//DO SOMETHING HERE THAT FITS FOR ALL KINDS OF PLAYER'S TYPE OBJECTS
}
How do I add a case sentence to expand it to handle several different player objects ?
Thanks !
The MediaPlayer attribute passed to this method is the same MediaPlayer that has just completed, so as long as you are keeping a pointer to each media player (such as through a global variable), then all you need to do is check which media player you have received:
public class MyClass implements OnClompleteListener
{
MediaPlayer player1, player2, player3;
//initialize them
player1.setOnCompleteListener(this);
player2.setOnCompleteListener(this);
player3.setOnCompleteListener(this);
#Override
public void onCompletion(MediaPlayer mp)
{
if (mp == player1)
{
//TODO handle player 1 completion
}
else if (mp == player2)
{
//TODO handle player 2 completion
}
else if (mp == player3)
{
//TODO handle player 3 completion
}
}
}
You can also handle this in-line, without implementing the OnCompleteListener:
player1.setOnCompleteListener(new OnCompleteListener() {
#Override
public void onCompletion(MediaPlayer mp)
{
//mp IS ALWAYS EQUAL TO player1!
}
});
You can either create a class that extends MediaPlayer and give it an attribute like a number to keep track of which player is which. Then you would check the player's number in the onCompletion() method. Otherwise, you could create different onCompletion listeners and set them to the different players.
I have created a simple Ukulele tuner (The market I think lacks a visually pleasing and extremely simple tuner).
Any how, firstly, through the developer console I can see that there is a Null Pointer Exception at the Button Onclick Events. I have not been able to recreate this, however it has been reported four times.
Secondly, looking at the log while using the app I can see this warning;
E/MP3Extractor(68): Unable to resync. Signalling end of stream.
This entry here MediaPlayer array causing null pointer in Android seems to be along the same lines.
How it works.
Through the use of radio buttons the user selects either to play a single note or a continuous note. I have created a subroutine called StopMediaPlayer that stops, resets and instantiates the MediaPlayers again. This was used because I could never seem to stop the continuous play back but only pause it.
Is the warning and the NullPointerException related? Is there a more efficient/better means of stopping MediaPlayer that will mean that I wont have to re instantiate the notes every time.
Thank You
One of the offending Onclicks
Button gButton = (Button) findViewById(R.id.gButton);
gButton.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
if (singleRadio.isChecked() == true)
{
StopMediaPLayer();
gNote.setLooping(false);
gNote.start();
}
else if (contRadio.isChecked() == true)
{
StopMediaPLayer();
gNote.setLooping(true);
gNote.start();
}
}
});
Stop Media Player Subroutine
public void StopMediaPLayer()
{
Log.i("UkuleleTuner", "Stop Media Player");
gNote.setLooping(false);
cNote.setLooping(false);
eNote.setLooping(false);
aNote.setLooping(false);
gNote.stop();
cNote.stop();
eNote.stop();
aNote.stop();
gNote.reset();
cNote.reset();
eNote.reset();
aNote.reset();
gNote = MediaPlayer.create(this, R.raw.g_note);
cNote = MediaPlayer.create(this, R.raw.c_note);
eNote = MediaPlayer.create(this, R.raw.e_note);
aNote = MediaPlayer.create(this, R.raw.a_note);
}
I'm trying to create a simple Sound-board Android app, using ListView items as buttons. (Btw, I'm a novice programmer)
The idea is that I press a button, and a specific sound file plays. If I press any button while a sound is playing, it should first stop that sound and then start to play the new one.
Currently the sounds play without stopping any currently playing sounds, so that if I spam the buttons I get multiple sounds playing at the same time (and if I press too many at once, the app force closes).
I have tried using a few variations of:
if (mp.isPlaying()) {
mp.stop();
}
But according to what I read on a few other sources, I am creating multiple instances of the MediaPlayer class, and even though they have the same name the stop() method tries to stop on the latest instance of mp (in some cases it isn't even created yet).
I'm guessing my general implementation of the MediaPlayer class is wrong, but it's the best I could figure out to do.
Anyways, here's the relevant block of code:
public class soundTest extends Activity {
private ListView lv1;
private String lv_arr[]={"test 1","test 2","test 3","test 4","test 5"};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv1=(ListView)findViewById(R.id.ListView01);
lv1.setAdapter(new ArrayAdapter<String>(this,R.layout.list_item, lv_arr));
lv1.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
if (lv1.getItemAtPosition(position)=="test 1") {
MediaPlayer mp = MediaPlayer.create(getApplicationContext(),R.raw.sound1);
mp.start();
mp.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
}
if (lv1.getItemAtPosition(position)=="test 2") {
MediaPlayer mp = MediaPlayer.create(getApplicationContext(),R.raw.sound2);
mp.start();
mp.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
}
//And the rest of the sounds 3,4,5.
}
});
}
}
Any help would be appreciated, thanks.
Edit (22nd March):
I've found the following piece of code that should work:
mp.setDataSource(context, Uri.parse("android.resource://" + Config.PACKAGE + "/" + resId));
But, I can't figure out how the "Config.PACKAGE" part works. I just get an error "PACKAGE cannot be resolved, or is not a field".
I tried replacing "PACKAGE" with the package name, same error. I also tried:
try {
mp.setDataSource(getApplicationContext(),Uri.parse("android.resource://com.mptest/" + R.raw.test2));
} catch (IOException e) {
e.printStackTrace();
}
But I can't work what exactly to put in place of "//com.mptest/".
The global variable MediaPlayer needs to be set private static. This has caught me several times.
Don't use a global. Use the singleton pattern. That is what it is for, so that you can have exactly one instance w/o using a global variable.
There are lots of good examples of Java code for this pattern, start with http://www.javaworld.com/javaworld/jw-04-2003/jw-0425-designpatterns.html and you can't go wrong.
Consider keeping your MediaPlayer as a global variable instead of having multiple instances of it.
You might find a good reference in the VideoView Source, since it handles one MediaPlayer object and you may change content during playback.
I am trying to create a music streaming app.
So far it works just fine.
I am using the
MediaPlayer.create(thisContext, Uri.parse(PATH_TO_STREAM));
convenience method to prepare the infinite stream (24x7 mp3 stream).
It hangs for just a few seconds on this call which I have neatly tucked into my startPlaying() method.
The button doesn't show it's getting clicked until after the stream starts playing so at first the user is left wondering if they tapped the button or missed.
So I want to update a TextView label next to the button that says "Wait..." or "Buffering" etc. then clear it after the stream starts so the user knows they pressed the button OK.
Even if I step through this in debug the label doesn't update until after the onClick is finished. I can comment out the last line that clears the label text and can see it set to "Buffering..." OK. But only after it exits the onClick. Is this a limitation of using the media player create() method?
final Button startbutton = (Button) findViewById(R.id.Button01);
this.tvBuffering = (TextView) findViewById(R.id.tvBuffering);
startbutton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
tvBuffering.setText("Buffering...");
//do something like invalidate() here??
startPlaying(); //blocks here for a few seconds to buffer then plays.
tvBuffering.setText(" "); //clear the text since it's playing by now.
}
});
It's not such a great idea to intentionally include that sort of delay in the UI, because that will block just about anything the user tries to do for those few seconds. I'm assuming that your startPlaying() includes a call to prepare(), as well as start(). When taking data from a source that won't be immediately available (such as a stream), you should use prepareAsync() instead, which will start preparation and return immediately instead of blocking until preparation is complete.
You can attach a callback to your MediaPlayer to then take action once preparation has completed through a MediaPlayer.OnPreparedListener
Here's a simple example. Note that your OnClickListener can stay the same, as long as you change the prepare() in the startPlaying() method to prepareAsync() and remove the start() call from startPlaying().
startbutton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
tvBuffering.setText("Buffering...");
startPlaying(); //which should call prepareAsync() instead of prepare()
//and have no call to start()
}
});
mYourMediaPlayer.setOnPreparedListener( new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
start();
tvBuffering.setText(" ");
}
});