Позиция и продолжительность ExoPlayer из уничтоженного сервиса каким-то образом сохраняютсяAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Позиция и продолжительность ExoPlayer из уничтоженного сервиса каким-то образом сохраняются

Сообщение Anonymous »

Есть фрагмент (AudioFragment.kt), в котором запускается сервис (AudioService.kt) с экземпляром ExoPlayer:

Код: Выделить всё

    class AudioFragment : Fragment() {

private lateinit var urlFromAPI: String

private var wasButtonClicked: Boolean = false

private var isBounded = false

private var audioService: AudioService? = null

private val serviceConnection: ServiceConnection = object : ServiceConnection {

override fun onServiceConnected(name: ComponentName?, iBinder: IBinder?) {
val localBinder = iBinder as AudioService.AudioBinder
audioService = localBinder.getService()

isBounded = true

urlFromAPI = activity?.intent?.getStringExtra("audio").toString()
startAudioService()
}

override fun onServiceDisconnected(name: ComponentName?) {
isBounded = false
audioService = null
stopAudioService()
}
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {

binding = FragmentAudioBinding.inflate(inflater, container, false)
val view = binding.root
return view

}

@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.tvAudioPosition.text = "00:00"
bindToAudioService()
binding.btnAudioOff.setOnClickListener {
if (wasButtonClicked) {
binding.btnAudioOff.setImageResource(R.drawable.baseline_pause_24)
wasButtonClicked = false
playAgainAudio()
} else {
binding.btnAudioOff.setImageResource(R.drawable.baseline_play_arrow_24)
wasButtonClicked = true
pauseAudio()
}
}

}

private fun unbindToAudioService() {
activity?.unbindService(serviceConnection)
}

private fun bindToAudioService() {

val intent = Intent(activity, AudioService::class.java)
activity?.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)

}

private fun startAudioService() {
val serviceIntent = Intent(activity, AudioService::class.java)

val dataFromAPI = activity?.intent?.getStringExtra("data").toString()
val titleFromAPI = activity?.intent?.getStringExtra("title").toString()
val urlFromAPI = activity?.intent?.getStringExtra("audio").toString()

serviceIntent.putExtra("data", dataFromAPI)
serviceIntent.putExtra("title", titleFromAPI)
serviceIntent.putExtra("audio", urlFromAPI)

serviceIntent.action = Constants.ACTION.STARTFOREGROUND_ACTION
activity?.startService(serviceIntent)
}

private fun pauseAudio() {
val serviceIntent = Intent(activity, AudioService::class.java)
serviceIntent.action = Constants.ACTION.PAUSEFOREGROUND_ACTION
activity?.startService(serviceIntent)
}

private fun playAgainAudio() {
val serviceIntent = Intent(activity, AudioService::class.java)
serviceIntent.action = Constants.ACTION.PLAYAGAINFOREGROUND_ACTION
activity?.startService(serviceIntent)
}

private fun stopAudioService() {
val serviceIntent = Intent(activity, AudioService::class.java)
activity?.stopService(serviceIntent)
}

override fun onDestroy() {
stopAudioService()
unbindToAudioService()
super.onDestroy()
}

companion object {
@SuppressLint("StaticFieldLeak")
lateinit var binding: FragmentAudioBinding
@JvmStatic
fun newInstance() = AudioFragment()
}
}
AudioService.kt:

Код: Выделить всё

    class AudioService :  Service() {

private lateinit var dataFromAPI: String
private lateinit var titleFromAPI: String
private lateinit var urlFromAPI: String

var mediaPlayer: ExoPlayer? = null

var duration: Int = 0
var currentPosition: Int = 0

private val audioBinder = AudioBinder()

@RequiresApi(Build.VERSION_CODES.O)
@SuppressLint("ForegroundServiceType")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

dataFromAPI = intent?.getStringExtra("data").toString()
titleFromAPI = intent?.getStringExtra("title").toString()
urlFromAPI = intent?.getStringExtra("audio").toString()

when (intent?.action) {
Constants.ACTION.STARTFOREGROUND_ACTION -> {
initialiseAudio(urlFromAPI)
startForeground(NOTIFICATION_ID, createNotification())
initialiseSeekBar()
}

Constants.ACTION.PAUSEFOREGROUND_ACTION -> {
pauseAudio()
startForeground(NOTIFICATION_ID, createNotification())
}

Constants.ACTION.PLAYAGAINFOREGROUND_ACTION -> {
playAudio()
startForeground(NOTIFICATION_ID, createNotification())
}

Constants.ACTION.STOPFOREGROUND_ACTION -> {
stopForeground(true)
pauseAudio()
stopSelf()
}
}
return START_NOT_STICKY
}

private fun initialiseAudio(urlFromAPI: String) {

mediaPlayer = ExoPlayer.Builder(this).build()
mediaPlayer!!.setMediaItem(MediaItem.fromUri(urlFromAPI))

try {
mediaPlayer!!.prepare()
mediaPlayer!!.play()
} catch (e: IOException) {
e.printStackTrace()
}
}

private fun playAudio() {
if (!isPlaying())
mediaPlayer!!.play()
}

private fun pauseAudio() {
if (isPlaying())
mediaPlayer!!.pause()
}

override fun onDestroy() {
mediaPlayer!!.playWhenReady = false
mediaPlayer!!.stop()
mediaPlayer!!.release()
super.onDestroy()
}

override fun onBind(intent: Intent?): IBinder {
return audioBinder
}

inner class AudioBinder : Binder() {
fun getService(): AudioService {
return this@AudioService
}
}

@OptIn(UnstableApi::class)
private fun initialiseSeekBar() {

mediaPlayer!!.addListener(object : Player.Listener {
@SuppressLint("UseCompatLoadingForDrawables")
@Deprecated("Deprecated in Java")
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
if (playbackState == Player.STATE_READY &&  mediaPlayer!!.playWhenReady) {
binding.btnAudioOff.setImageDrawable(resources.getDrawable(R.drawable.baseline_pause_24))
} else {
binding.btnAudioOff.setImageDrawable(resources.getDrawable(R.drawable.baseline_play_arrow_24))
}
}

override fun onIsPlayingChanged(isPlaying: Boolean) {
duration = mediaPlayer!!.duration.toInt() / 1000
binding.skAudioSeekBar.max = duration
binding.tvAudioDuration.text = getTimeString(duration)
}

override fun onPositionDiscontinuity(
oldPosition: Player.PositionInfo,
newPosition: Player.PositionInfo,
reason: Int
) {
currentPosition = mediaPlayer!!.currentPosition.toInt() / 1000
binding.skAudioSeekBar.progress = currentPosition
binding.tvAudioPosition.text = getTimeString(currentPosition)
binding.tvAudioDuration.text = getTimeString(mediaPlayer!!.duration.toInt() / 1000)
}

})

binding.skAudioSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) {
mediaPlayer!!.seekTo(progress.toLong() * 1000)
binding.tvAudioPosition.text = getTimeString(progress)
binding.tvAudioDuration.text = getTimeString(duration)
}
}

override fun onStartTrackingTouch(p0: SeekBar?) {

}

override fun onStopTrackingTouch(p0: SeekBar?) {

}

})

val handler = Handler(Looper.getMainLooper())
handler.post(object : Runnable {
override fun run() {
currentPosition = mediaPlayer!!.currentPosition.toInt() / 1000
binding.skAudioSeekBar.progress = currentPosition
binding.tvAudioPosition.text = getTimeString(currentPosition)
binding.tvAudioDuration.text = getTimeString(duration)
handler.postDelayed(this, 1000)
}

})
}

@SuppressLint("DefaultLocale")
fun getTimeString(duration: Int): String {
val min = duration / 60
val sec = duration % 60
val time = String.format("%02d:%02d", min, sec)
return time
}

@OptIn(UnstableApi::class)
@Suppress("DEPRECATED_IDENTITY_EQUALS")
private fun isPlaying(): Boolean {
return mediaPlayer!!.playbackState === Player.STATE_READY && mediaPlayer!!.playWhenReady
}
}
Когда AudioService.kt вызывается в первый раз, все работает нормально, но когда вы вызываете его снова, TextView с позицией и длительность звуковой дорожки начинает «мигать»: сначала (на секунду) показывается положение и длительность, актуальная для предыдущего запуска сервиса, а затем (на секунду) — для текущего. Вместе с ним «подпрыгивает» SeekBar.
Я правда не могу понять, как и почему система сохраняет позицию и продолжительность ExoPlayer из уже уничтоженный сервис (даже после перезапуска приложения).

Подробнее здесь: https://stackoverflow.com/questions/787 ... mehow-save
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»