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.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import androidx.annotation.OptIn
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
@ -43,8 +41,6 @@ import kotlinx.coroutines.withContext
* A service to play downloaded audio in the background
*/
class OfflinePlayerService : LifecycleService() {
val handler = Handler(Looper.getMainLooper())
private var player: ExoPlayer? = null
private var nowPlayingNotification: NowPlayingNotification? = null
private lateinit var videoId: String
@ -209,14 +205,18 @@ class OfflinePlayerService : LifecycleService() {
saveWatchPosition()
nowPlayingNotification?.destroySelf()
player?.stop()
player?.release()
player = null
nowPlayingNotification = null
watchPositionTimer.destroy()
unregisterReceiver(playerActionReceiver)
runCatching {
player?.stop()
player?.release()
}
player = null
runCatching {
unregisterReceiver(playerActionReceiver)
}
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
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.Streams
import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.DatabaseHelper
import com.github.libretube.enums.NotificationId
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.helpers.PlayerHelper
import com.github.libretube.helpers.PlayerHelper.checkForSegments
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.obj.PlayerNotificationData
import com.github.libretube.parcelable.PlayerData
@ -372,7 +370,7 @@ class OnlinePlayerService : LifecycleService() {
val (uri, mimeType) =
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 {
ProxyHelper.unwrapStreamUrl(streams.hls.orEmpty())
.toUri() to MimeTypes.APPLICATION_M3U8
@ -427,12 +425,17 @@ class OnlinePlayerService : LifecycleService() {
PlayingQueue.resetToDefaults()
if (this::nowPlayingNotification.isInitialized) nowPlayingNotification.destroySelf()
player?.stop()
player?.release()
watchPositionTimer.destroy()
unregisterReceiver(playerActionReceiver)
handler.removeCallbacksAndMessages(null)
runCatching {
player?.stop()
player?.release()
}
runCatching {
unregisterReceiver(playerActionReceiver)
}
// called when the user pressed stop in 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() {
saveWatchPosition()
playerViewModel.player = null
player.release()
watchPositionTimer.destroy()
nowPlayingNotification?.destroySelf()
nowPlayingNotification = null
watchPositionTimer.destroy()
unregisterReceiver(playerActionReceiver)
playerViewModel.player = null
runCatching {
player.stop()
player.release()
}
runCatching {
unregisterReceiver(playerActionReceiver)
}
super.onDestroy()
}

View File

@ -783,18 +783,31 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
override fun onDestroy() {
super.onDestroy()
saveWatchPosition()
viewModel.nowPlayingNotification?.destroySelf()
viewModel.nowPlayingNotification = null
watchPositionTimer.destroy()
handler.removeCallbacksAndMessages(null)
if (this::exoPlayer.isInitialized) {
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()
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) {
// disable the auto PiP mode for SDK >= 32
PictureInPictureCompat
@ -802,35 +815,29 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
}
}
handler.removeCallbacksAndMessages(null)
runCatching {
// unregister the receiver for player actions
context?.unregisterReceiver(playerActionReceiver)
}
// restore the orientation that's used by the main activity
(context as MainActivity).requestOrientationChange()
_binding = null
stopVideoPlay()
watchPositionTimer.destroy()
}
private fun stopVideoPlay() {
try {
saveWatchPosition()
private fun killPlayerFragment() {
viewModel.isFullscreen.value = false
viewModel.isMiniPlayerVisible.value = false
viewModel.nowPlayingNotification?.destroySelf()
viewModel.nowPlayingNotification = null
// 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
if (!viewModel.isOrientationChangeInProgress) {
exoPlayer.stop()
exoPlayer.release()
}
(context as MainActivity).requestOrientationChange()
} catch (e: Exception) {
e.printStackTrace()
binding.playerMotionLayout.transitionToEnd()
mainActivity.supportFragmentManager.commit {
remove(this@PlayerFragment)
}
}
@ -1512,22 +1519,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
!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
* If true, the activity will be automatically restarted