问题
I'm learning android and currently RecyclerView, so in this example I'm playing a specific raw sound file when clicking a play button and then releasing it when it's done. I could seriously use some pointers on what is the best practice however as I have a few issues and questions:
- I have a problem with transiting states so if I press play very quickly I'll get an IllegalStateException, probably from trying to release an already released player.
- I should apparently also nullify after release and do a null check prior but since I'm using it in a nested scope it has to be declared final and can't be nulled which is confusing, I've obviously missed something.
- setting the clickListner on an item in onBindViewHolder also seems significantly slower and less responsive than adding a clickListner on the entire row in the viewHolder class.
What is the best practice for setting up your mediaPlayer and managing your resources correctly with RecyclerView and what am I doing wrong that would cause crashes and "slowness"?
//ViewHolder
public static class myHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
ImageView playBtn;
public myHolder(View v){
super(v);
playBtn = (ImageView) v.findViewById(R.id.playBtn);
v.setOnClickListener(this);
}
@Override
public void onClick(View v) {
}
}
//onBindViewHolder / onCompletionListener
@Override
public void onBindViewHolder(myHolder holder, int position) {
myClass item = itemList.get(position);
final MediaPlayer mediaPlayer = MediaPlayer.create(holder.itemView.getContext(), item.getAudioSource());
holder.playBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
mediaPlayer.start();
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
}
});
}
回答1:
If you're only listening in user interaction you can drop setOnCompleteListener()
and instead define your play/stop code in (in order) OnLongClickListener()
and OnClickListener
. Like this:
holder.playBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
//Code to stop playing and release resources
}
});
holder.playBtn.setOnLongClickListener(new View.OnClickListener(){
@Override
public void onLongClick(View v) {
//Code to start playing
return false; //!!This is important so that OnClick() would be called after releasing the button
}
});
回答2:
OBS As I'm answering my own question I can NOT say that this is acctualy good practice but it did solve the issues I was having, if anyone spots an error or has a better more efficient way then please post an answer and elaborate.
- instead of declaring the MediaPlayer inside the onBindViewHolder I made it a class variable, this allowed me to avoid having it final and be able to nullify it after release.
- I then defined it inside the onClick method instead to make sure that it would never be null and always be declared when needed.
- I had to make my holder and "item" final in order to use them inside the onClick but this seems to have worked (recyclerView magic I guess)
The app no longer crashes and onCompletionListener should clear all resources, this is the current code:
//class variable mediaplayer and onCompletionListener method
MediaPlayer mediaPlayer;
//onCompletionListener method
MediaPlayer.OnCompletionListener mCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mp.release();
mediaPlayer = null;
}
};
//onBindViewHolder
@Override
public void onBindViewHolder(final itemHolder holder, int position) {
final Items item = mItemList.get(position);
holder.playBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
mediaPlayer = MediaPlayer.create(holder.itemView.getContext(), item.getAudioSource());
if(mediaPlayer != null) {
mediaPlayer.start();
mediaPlayer.setOnCompletionListener(mCompletionListener);
}
}
});
}
来源:https://stackoverflow.com/questions/46040158/playing-and-releasing-audio-in-recyclerview