fix: crash when ending player service / fragment

This commit is contained in:
Bnyro 2024-05-08 16:05:55 +02:00
parent df087054d9
commit a56b8ccf0b
4 changed files with 66 additions and 65 deletions

View File

@ -4,9 +4,7 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.os.Looper
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
@ -43,8 +41,6 @@ import kotlinx.coroutines.withContext
* A service to play downloaded audio in the background * A service to play downloaded audio in the background
*/ */
class OfflinePlayerService : LifecycleService() { class OfflinePlayerService : LifecycleService() {
val handler = Handler(Looper.getMainLooper())
private var player: ExoPlayer? = null private var player: ExoPlayer? = null
private var nowPlayingNotification: NowPlayingNotification? = null private var nowPlayingNotification: NowPlayingNotification? = null
private lateinit var videoId: String private lateinit var videoId: String
@ -209,14 +205,18 @@ class OfflinePlayerService : LifecycleService() {
saveWatchPosition() saveWatchPosition()
nowPlayingNotification?.destroySelf() nowPlayingNotification?.destroySelf()
player?.stop()
player?.release()
player = null
nowPlayingNotification = null nowPlayingNotification = null
watchPositionTimer.destroy() watchPositionTimer.destroy()
unregisterReceiver(playerActionReceiver)
runCatching {
player?.stop()
player?.release()
}
player = null
runCatching {
unregisterReceiver(playerActionReceiver)
}
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf() stopSelf()

View File

@ -31,7 +31,6 @@ import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.Segment import com.github.libretube.api.obj.Segment
import com.github.libretube.api.obj.Streams import com.github.libretube.api.obj.Streams
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.DatabaseHelper import com.github.libretube.db.DatabaseHelper
import com.github.libretube.enums.NotificationId import com.github.libretube.enums.NotificationId
import com.github.libretube.enums.PlayerEvent import com.github.libretube.enums.PlayerEvent
@ -42,7 +41,6 @@ import com.github.libretube.extensions.toID
import com.github.libretube.extensions.updateParameters import com.github.libretube.extensions.updateParameters
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PlayerHelper.checkForSegments import com.github.libretube.helpers.PlayerHelper.checkForSegments
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ProxyHelper import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.obj.PlayerNotificationData import com.github.libretube.obj.PlayerNotificationData
import com.github.libretube.parcelable.PlayerData import com.github.libretube.parcelable.PlayerData
@ -372,7 +370,7 @@ class OnlinePlayerService : LifecycleService() {
val (uri, mimeType) = val (uri, mimeType) =
if (!PlayerHelper.useHlsOverDash && streams.audioStreams.isNotEmpty() && !PlayerHelper.disablePipedProxy) { if (!PlayerHelper.useHlsOverDash && streams.audioStreams.isNotEmpty() && !PlayerHelper.disablePipedProxy) {
PlayerHelper.createDashSource(streams, this,) to MimeTypes.APPLICATION_MPD PlayerHelper.createDashSource(streams, this) to MimeTypes.APPLICATION_MPD
} else { } else {
ProxyHelper.unwrapStreamUrl(streams.hls.orEmpty()) ProxyHelper.unwrapStreamUrl(streams.hls.orEmpty())
.toUri() to MimeTypes.APPLICATION_M3U8 .toUri() to MimeTypes.APPLICATION_M3U8
@ -427,12 +425,17 @@ class OnlinePlayerService : LifecycleService() {
PlayingQueue.resetToDefaults() PlayingQueue.resetToDefaults()
if (this::nowPlayingNotification.isInitialized) nowPlayingNotification.destroySelf() if (this::nowPlayingNotification.isInitialized) nowPlayingNotification.destroySelf()
player?.stop()
player?.release()
watchPositionTimer.destroy() watchPositionTimer.destroy()
unregisterReceiver(playerActionReceiver) handler.removeCallbacksAndMessages(null)
runCatching {
player?.stop()
player?.release()
}
runCatching {
unregisterReceiver(playerActionReceiver)
}
// called when the user pressed stop in the notification // called when the user pressed stop in the notification
// stop the service from being in the foreground and remove the notification // stop the service from being in the foreground and remove the notification

View File

@ -300,12 +300,19 @@ class OfflinePlayerActivity : BaseActivity() {
override fun onDestroy() { override fun onDestroy() {
saveWatchPosition() saveWatchPosition()
playerViewModel.player = null
player.release()
watchPositionTimer.destroy()
nowPlayingNotification?.destroySelf() nowPlayingNotification?.destroySelf()
nowPlayingNotification = null
watchPositionTimer.destroy()
unregisterReceiver(playerActionReceiver) playerViewModel.player = null
runCatching {
player.stop()
player.release()
}
runCatching {
unregisterReceiver(playerActionReceiver)
}
super.onDestroy() super.onDestroy()
} }

View File

@ -783,18 +783,31 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
saveWatchPosition()
viewModel.nowPlayingNotification?.destroySelf()
viewModel.nowPlayingNotification = null
watchPositionTimer.destroy()
handler.removeCallbacksAndMessages(null)
if (this::exoPlayer.isInitialized) { if (this::exoPlayer.isInitialized) {
exoPlayer.removeListener(playerListener) exoPlayer.removeListener(playerListener)
// the player could also be a different instance because a new player fragment
// got created in the meanwhile
if (!viewModel.isOrientationChangeInProgress && viewModel.player == exoPlayer) {
viewModel.player = null
viewModel.trackSelector = null
}
exoPlayer.pause() exoPlayer.pause()
runCatching {
// the player could also be a different instance because a new player fragment
// got created in the meanwhile
if (!viewModel.isOrientationChangeInProgress) {
if (viewModel.player == exoPlayer) {
viewModel.player = null
viewModel.trackSelector = null
}
exoPlayer.stop()
exoPlayer.release()
}
}
if (PlayerHelper.pipEnabled) { if (PlayerHelper.pipEnabled) {
// disable the auto PiP mode for SDK >= 32 // disable the auto PiP mode for SDK >= 32
PictureInPictureCompat PictureInPictureCompat
@ -802,35 +815,29 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
} }
} }
handler.removeCallbacksAndMessages(null)
runCatching { runCatching {
// unregister the receiver for player actions // unregister the receiver for player actions
context?.unregisterReceiver(playerActionReceiver) context?.unregisterReceiver(playerActionReceiver)
} }
// restore the orientation that's used by the main activity
(context as MainActivity).requestOrientationChange()
_binding = null _binding = null
stopVideoPlay()
watchPositionTimer.destroy()
} }
private fun stopVideoPlay() { private fun killPlayerFragment() {
try { viewModel.isFullscreen.value = false
saveWatchPosition() viewModel.isMiniPlayerVisible.value = false
viewModel.nowPlayingNotification?.destroySelf() // dismiss the fullscreen dialog if it's currently visible
viewModel.nowPlayingNotification = null // otherwise it would stay alive while being detached from this fragment
fullscreenDialog.dismiss()
binding.player.currentWindow = null
if (!viewModel.isOrientationChangeInProgress) { binding.playerMotionLayout.transitionToEnd()
exoPlayer.stop() mainActivity.supportFragmentManager.commit {
exoPlayer.release() remove(this@PlayerFragment)
}
(context as MainActivity).requestOrientationChange()
} catch (e: Exception) {
e.printStackTrace()
} }
} }
@ -1512,22 +1519,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
!BackgroundHelper.isBackgroundServiceRunning(requireContext()) !BackgroundHelper.isBackgroundServiceRunning(requireContext())
} }
private fun killPlayerFragment() {
stopVideoPlay()
viewModel.isFullscreen.value = false
viewModel.isMiniPlayerVisible.value = false
// dismiss the fullscreen dialog if it's currently visible
// otherwise it would stay alive while being detached from this fragment
fullscreenDialog.dismiss()
binding.player.currentWindow = null
binding.playerMotionLayout.transitionToEnd()
mainActivity.supportFragmentManager.commit {
remove(this@PlayerFragment)
}
}
/** /**
* Check if the activity needs to be recreated due to an orientation change * Check if the activity needs to be recreated due to an orientation change
* If true, the activity will be automatically restarted * If true, the activity will be automatically restarted