well.. it's not exactly a playlist but the idea is similar. I'm making an audio book. And the audio data is huge (can get to 90 MB per book) so I don't want to load all of this in memory because I'm aware of the memory restrictions these devices have.
So instead of that I thought about getting a batch of sentences (yes, the audio data is splitted in sentences and stored in a local sqlite database as blob), lets say 10 or 20 and put them in a byte array, saving each one of those 10 or 20 byte arrays into a temporal file (each at a time, like explained here for one single byte array Android - Playing mp3 from byte[]) and then give that file to the MediaPlayer.
The problem is I need to wait for one sentence to end for the next one to begin, so I don't overlap the sounds. For that I made the class which contains the MeadiaReader to implement OnCompletionListener. So whenever a sentence ends, the onCompletion method is called. This method, starts another line, which in turn calls onCopletion at the end, and so it goes.
At the end it works, but it goes kind of recursive and I don't like the way it ended up being architected. Any ideas of how could I better implement this kind of functionality in the app?
--------------------------------------- EDIT --------------------------------------
Well, here's some code for clarification. I just got this NullPointerException which returned this stacktrace:
06-22 12:16:09.152: ERROR/AndroidRuntime(1163): java.lang.NullPointerException
06-22 12:16:09.152: ERROR/AndroidRuntime(1163): at com.spiral.android.IslamicAudioBook.SentencesReader.onCompletion(SentencesReader.java:163)
06-22 12:16:09.152: ERROR/AndroidRuntime(1163): at com.spiral.android.IslamicAudioBook.SentencesReader.playMp3(SentencesReader.java:123)
06-22 12:16:09.152: ERROR/AndroidRuntime(1163): at com.spiral.android.IslamicAudioBook.SentencesReader.onCompletion(SentencesReader.java:163)
06-22 12:16:09.152: ERROR/AndroidRuntime(1163): at com.spiral.android.IslamicAudioBook.SentencesReader.playMp3(SentencesReader.java:123)
.... (keeps going like this for another 10 lines or so). The exception itself is easy to understand, and It just pointed out the fact that function call is going recursive. playMp3 method is very similar to the one from the link I passed above, and onCompletion is somewhat like this:
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
if(batchSize*batchIndex + sentenceIndex > totalNumberOfSentences){
return;
}
if(batchOfSounds == null){
return;
}
if(sentenceIndex == this.batchSize - 1){
Log.d(TAG,"All sentences from batch read, getting next batch");
this.batchOfSounds = this.getBatchOfSoundsFromDatabase(batchIndex);
}else{
Log.d(TAG,"Playing sentence index: "+sentenceIndex);
}
this.playMp3(batchOfSounds[sentenceIndex],0);
}
As you can easily see the exception is raised just because I dumbly check batchOfSounds (which is a byte[][]) for being null before the method getBatchOfSoundsFromDatabase returns a new reference to it, so don't mind about it, I will fix it.
But then this 2 methods are calling each other and the thing is clearly going recursive right? and I suppose that is NOT good at all.. right? or is it acceptable if I just don't create new references in both methods? stretching this question a little bit more, when is it acceptable to have a recursive method? come to think about it now I believe that if no new instance variables are created it might be acceptable. Am I right?
The way you have it sounds reasonable, although I'm not 100% sure I know what you mean without a little code or pseudocode. Still, if you don't like how it's structured, how about this: you can have a class that wraps the media player that maintains a queue of stuff to play. It still listens to onCompletion, this simply gets the next item off the queue and plays it. You can keep a separate "loader" sort of class which populates the queue onCompletion. This way you can separate concerns, keeping the player concerned about playing and a loader class with only loading.