There is a service in which a Media Player
instance is created to play audio from the server:
class AudioService : Service() {
private lateinit var mediaPlayer: MediaPlayer
private val CHANNEL_ID = "AudioService"
private val audioBinder = AudioBinder()
fun initialiseAudio(urlFromAPI: String) {
mediaPlayer = MediaPlayer()
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC)
try {
mediaPlayer.setDataSource(urlFromAPI)
mediaPlayer.prepare()
mediaPlayer.start()
} catch (e: IOException) {
e.printStackTrace()
}
}
fun playAudio() {
if (!mediaPlayer.isPlaying)
mediaPlayer.start()
}
fun pauseAudio() {
if (mediaPlayer.isPlaying)
mediaPlayer.pause()
}
fun getMP(): MediaPlayer {
return mediaPlayer
}
fun getMPDuration(): Int {
return mediaPlayer.duration
}
fun getMPPosition(): Int {
return mediaPlayer.currentPosition
}
override fun onDestroy() {
mediaPlayer.reset()
super.onDestroy()
}
override fun onBind(intent: Intent?): IBinder {
return audioBinder
}
inner class AudioBinder : Binder() {
fun getService(): AudioService {
return this@AudioService
}
}
}
The service connects in the fragment by bindService
.
I must create a SeekBar
and two TextView
with an audio’s position and duration in the fragment.
SeekBar
‘s parameters in onViewCreated
of the fragment:
binding.skAudioSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) audioService!!.getMP().seekTo(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
if (seekBar.progress > 70) {
ObjectAnimator.ofInt(seekBar, "progress", 100).setDuration(100).start()
} else if (seekBar.progress < 30) {
ObjectAnimator.ofInt(seekBar, "progress", 0).setDuration(100).start()
} else {
ObjectAnimator.ofInt(seekBar, "progress", 50).setDuration(100).start()
}
}
The method for initialization of the SeekBar in the fragment:
private fun initialiseSeekBar() {
binding.skAudioSeekBar.max = audioService!!.getMPDuration()
val handler = Handler()
handler.postDelayed(object : Runnable {
override fun run() {
try {
binding.skAudioSeekBar.progress = audioService!!.getMPPosition()
handler.postDelayed(this, 1000)
val durationLength: Int = audioService!!.getMPDuration()
val durationText: String =
DateUtils.formatElapsedTime((durationLength / 1000).toLong())
binding.tvAudioDuration.text = durationText
val positionLength: Int = audioService!!.getMPPosition()
val positionText: String =
DateUtils.formatElapsedTime((positionLength / 1000).toLong())
binding.tvAudioPosition.text = positionText
} catch (e: Exception) {
binding.skAudioSeekBar.progress = 0
}
}
}, 0)
}
The problem is that the app crashes when the service starts (FATAL EXCEPTION: main java.lang.NullPointerException
). LogCat points to the first line of the initialiseSeekBar()
. If this method is not used, the service starts normally.