Search code examples
androidkotlinandroid-activityexoplayer2.xandroid-service-binding

Binding exoplayer from service to activity not working?


Following is my service class

class LocalAudioService : Service() {

     var player: SimpleExoPlayer? = null
    private var playerNotificationManager: PlayerNotificationManager? = null
    private var mediaSession: MediaSessionCompat? = null
    private var mediaSessionConnector: MediaSessionConnector? = null
    private var allInOneApplication: AllInOneApplication? = null

    private val binder = LocalBinder()

    override fun onDestroy() {
        mediaSession?.release()
        mediaSessionConnector?.setPlayer(null, null)
        playerNotificationManager?.setPlayer(null)
        player?.release()
        player = null
        super.onDestroy()
    }

    override fun onBind(p0: Intent?): IBinder? {
        return binder
    }

    fun getPlayerInstance(): SimpleExoPlayer? {
        if (player == null) {
            startPlayer()
        }
        return player
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if(player == null){
            startPlayer()
        }
        return START_STICKY
    }

    private fun startPlayer(){
        val videoTrackSelectionFactory = AdaptiveTrackSelection.Factory()
        val trackSelector = DefaultTrackSelector(this, videoTrackSelectionFactory)
        allInOneApplication = AllInOneApplication.getInstance()
        val renderersFactory = (application as AllInOneApplication).buildRenderersFactory(false)
        player =
            SimpleExoPlayer.Builder(this, renderersFactory).setTrackSelector(trackSelector).build()
        val dataSourceFactory = DefaultDataSourceFactory(
            this,
            Util.getUserAgent(this, getString(R.string.app_name))
        )


//        val cacheDataSourceFactory = CacheDataSourceFactory(
//            DownloadUtil.getCache(this),
//            dataSourceFactory,
//            CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR
//        )
        val concatenatingMediaSource = ConcatenatingMediaSource()
        allInOneApplication?.localAudios?.map {
            val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
                .createMediaSource(it?.audioUri)
            concatenatingMediaSource.addMediaSource(mediaSource)
        }

        player?.prepare(concatenatingMediaSource)

        player?.playWhenReady = true

        playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
            this,
            AUDIO_CHANNEL_ID,
            R.string.audio_channel_name,
            R.string.audio_channel_name,
            NOTIFICATION_ID,
            object : MediaDescriptionAdapter {
                override fun createCurrentContentIntent(player: Player): PendingIntent? {
                    val intent = Intent(this@LocalAudioService, LocalAudioPlayer::class.java)
                    return PendingIntent.getActivity(
                        this@LocalAudioService,
                        0,
                        intent,
                        PendingIntent.FLAG_UPDATE_CURRENT
                    )
                }

                override fun getCurrentContentText(player: Player): CharSequence? {
                    return allInOneApplication?.localAudios?.get(player.currentWindowIndex)?.title
                }

                override fun getCurrentContentTitle(player: Player): CharSequence {
                    return allInOneApplication?.localAudios?.get(player.currentWindowIndex)?.title.toString()
                }

                override fun getCurrentLargeIcon(
                    player: Player,
                    callback: PlayerNotificationManager.BitmapCallback
                ): Bitmap? {
                    return BitmapFactory.decodeResource(resources, R.drawable.audio)
                }

            }, object : PlayerNotificationManager.NotificationListener {
                override fun onNotificationPosted(
                    notificationId: Int,
                    notification: Notification,
                    ongoing: Boolean
                ) {
                    startForeground(notificationId, notification)
                }

                override fun onNotificationCancelled(
                    notificationId: Int,
                    dismissedByUser: Boolean
                ) {
                    stopSelf()

                }
            }
        )

        playerNotificationManager?.setPlayer(player)

        mediaSession = MediaSessionCompat(this, MEDIA_SESSION_TAG)
        mediaSession?.isActive = true
        mediaSession?.sessionToken?.let { playerNotificationManager?.setMediaSessionToken(it) }

        mediaSessionConnector = MediaSessionConnector(mediaSession)


        mediaSessionConnector?.setQueueNavigator(object : TimelineQueueNavigator(mediaSession) {
            override fun getMediaDescription(
                player: Player?,
                windowIndex: Int
            ): MediaDescriptionCompat? {
                return allInOneApplication?.getMediaDescription(
                    this@LocalAudioService,
                    allInOneApplication?.localAudios?.get(windowIndex)
                )
            }

        })
        mediaSessionConnector?.setPlayer(player, null)

    }


    inner class LocalBinder : Binder() {
        val service: LocalAudioService
            get() = this@LocalAudioService
    }

}

Following is my Activity class

class LocalAudioPlayer : AppCompatActivity() {

    private var localAudioService: LocalAudioService? = null
    private var mBound = false

    private val mConnection: ServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) {
            val binder: LocalAudioService.LocalBinder = iBinder as LocalAudioService.LocalBinder
            localAudioService = binder.service
            mBound = true
            initializePlayer()
        }

        override fun onServiceDisconnected(componentName: ComponentName) {
            mBound = false
        }
    }

    private fun initializePlayer() {
        if (mBound) {
            val player: SimpleExoPlayer? = localAudioService?.getPlayerInstance()
            exoplayer_local_audio_player.player = player
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_local_audio_player)

        Intent(this,LocalAudioService::class.java).also {
            Util.startForegroundService(this,it)
            exoplayer_local_audio_player.useController = true
            exoplayer_local_audio_player.showController()
            exoplayer_local_audio_player.controllerAutoShow = true
            exoplayer_local_audio_player.controllerHideOnTouch = false
        }
    }

    override fun onStart() {
        super.onStart()
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE)
        initializePlayer()
        //TODO do any UI setup
    }

    override fun onStop() {
        unbindService(mConnection)
        mBound = false
        super.onStop()
    }
}

I tried following this question Binding PlayerView with SimpleExoPlayer from a service but the sole does not works for me


Solution

  • Try this:

    override fun onStart() {
        super.onStart()
        Intent(this, LocalAudioService::class.java).also { intent ->
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
        }
    }
    

    initializePlayer() should not be called here. At this moment the Service is not bound yet. When the service is bound it will called in the onServiceConnected callback.