1

When I first open the Activity it creates an MediaBrowserService. When i try to reopen it i wanted to reconnect to it. I tried mutliple ways:

override fun onResume(){
mMediaBrowser.connect()
buildTransportControls()
}

When i try doing this it always brings the Error Message:

connect() called while not disconnected (state=CONNECT_STATE_CONNECTING)

How can this be even through i call in the onStop() function mMediaBrowser.disconnect() ? Or is this just the wrong way? This all happens when I go back via BackButton.

My Full code:

class MusicPlayer : AppCompatActivity() {



    private var songAdapter : RecyclerView.Adapter<MusicRecyclerSongAdapter.ViewHolder>? = null
    private var albumAdapter: RecyclerView.Adapter<MusicRecyclerAlbumAdapter.ViewHolder>? = null
    val context:Context = this

    //Media Browser
    private lateinit var mMediaBrowser: MediaBrowserCompat
    //prepare variables for late init
    lateinit var songUri:ArrayList<String>
    lateinit var album_name:ArrayList<String>
    lateinit var mediaController : MediaControllerCompat
    //------------------------------------------------------------------------------
    fun buildTransportControls(){
        mMediaBrowser.subscribe(mMediaBrowser.root,object: MediaBrowserCompat.SubscriptionCallback(){})

        mediaController = MediaControllerCompat.getMediaController(this@MusicPlayer)
        //Show Init state
        var metadat = mediaController.metadata
        var pbState = mediaController.playbackState

        //Register Callback to stay synced
        mediaController.registerCallback(controllerCallback)
    }
    //------------------------------------------------------------------------------
    val controllerCallback = object : MediaControllerCompat.Callback() {
        override fun onMetadataChanged(metadat: MediaMetadataCompat){

            Log.i("Musik",metadat.toString())
        }

        override fun onPlaybackStateChanged(state :PlaybackStateCompat){


        }
    }
    //------------------------------------------------------------------------------
    private val mConnectionCallbacks = object : MediaBrowserCompat.ConnectionCallback() {
        override fun onConnected() {
            Log.i("Connection","Connecting")
            // Get the token for the MediaSession
            val token = mMediaBrowser.sessionToken

            // Create a MediaControllerCompat
            val mediaController = MediaControllerCompat(this@MusicPlayer,token)

            // Save the controller
            MediaControllerCompat.setMediaController(this@MusicPlayer, mediaController)

            // Finish building the UI
            buildTransportControls()
        }

        override fun onConnectionSuspended() {
            // The Service has crashed. Disable transport controls until it automatically reconnects
            Log.i("ERROR","Connection Suspended")
        }

        override fun onConnectionFailed() {
            // The Service has refused our connection
            Log.i("ERROR","Connection Failed")
        }
    }
    //------------------------------------------------------------------------------
    fun startSong(position: Int){
        if(songAdapter == null) {
            songAdapter = MusicRecyclerSongAdapter(context, album_name[position])
            //Getting the Location of the Songs
            songUri = GetMusic.uris

            recyclerViewMusic.adapter = songAdapter

        }else{

            var pbSate = MediaControllerCompat.getMediaController(this@MusicPlayer).playbackState.playbackState
            MediaControllerCompat.getMediaController(this@MusicPlayer).transportControls.playFromUri(Uri.parse(songUri[position]), null)
            //TODO Testing if no song is playing already

        }

    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_music_player)
        //Initialize MediaBrowserServiceCompat
        if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
            //Create MediaBrowser Service
            createMediaBrowser()

        }else {
            permissionRequest()
        }

        //Create Recycler View and get Adapter
        //val recyclerViewMusic: RecyclerView = findViewById(R.id.recyclerViewMusic)
        recyclerViewMusic.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL,false)
        albumAdapter = MusicRecyclerAlbumAdapter(context)


        //Getting values from Companion Object
        album_name = GetAlbum.albums


        //Apply Adapter to Recyclerview and change Backgroundcolor
        recyclerViewMusic.adapter = albumAdapter
        recyclerViewMusic.setBackgroundColor(Color.BLUE)




        recyclerViewMusic.addOnItemTouchListener(
                RecyclerItemClickListener(context, recyclerViewMusic, object : RecyclerItemClickListener.OnItemClickListener {
                    override fun onItemClick(view: View, position: Int) {
                        //Change to Song selection
                        startSong(position)
                    }

                    override fun onLongItemClick(view: View, position: Int) {
                        //Change to Song selection
                        startSong(position)

                    }
                }
                )
        )
        musicprogressbar.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener{

            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                if(fromUser) {

                    //mplayer.seekTo(progress)
                }
            }

            override fun onStartTrackingTouch(seekBar: SeekBar?) {
                //if(::mplayer.isInitialized) {
                  //  mplayer.pause()
                //}
            }

            override fun onStopTrackingTouch(seekBar: SeekBar?) {
                //if(::mplayer.isInitialized) {
                  //  mplayer.start()                }

            }

        }
        )



    }
    override fun onStart() {
        super.onStart()

        mMediaBrowser.connect()

    }

    override fun onResume() {
        super.onResume()
        Log.i("I","RE OPEN")
        Log.i("MediaBrowserStatus",mMediaBrowser.isConnected.toString())
        if(!mMediaBrowser.isConnected) {
            mMediaBrowser.connect()
        }
        buildTransportControls()
        volumeControlStream = AudioManager.STREAM_MUSIC

    }



    override fun onStop() {
        super.onStop()
        Log.i("STOP","STOP")
        if(MediaControllerCompat.getMediaController(this@MusicPlayer) != null){
            MediaControllerCompat.getMediaController(this@MusicPlayer).unregisterCallback(controllerCallback)

        }
        mMediaBrowser.disconnect()
    }


    fun permissionRequest(){
        val permissions = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)

        if(permissions != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this, arrayOf(
                    Manifest.permission.READ_EXTERNAL_STORAGE
            ),101)
        }



    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        createMediaBrowser()

    }

    fun createMediaBrowser(){
        Log.i("HI","HI")
        mMediaBrowser = MediaBrowserCompat(this,
                ComponentName(this, MediaPlaybackService::class.java),
                mConnectionCallbacks,
                null)
    }


    fun musicControls(view: View) {
        //applying functions to buttons
        if (mMediaBrowser.isConnected) {

            when (view.id) {
                //Pause or Start Music

                R.id.musicStart -> if (mediaController.playbackState.playbackState != null) {
                    mediaController.transportControls.play()
                    Log.i("TEST",mediaController.playbackState.playbackState.toString())}
                R.id.musicPause -> if (mediaController.playbackState.playbackState != null) {
                    mediaController.transportControls.pause()
                    Log.i("TEST",mediaController.playbackState.playbackState.toString())}
                /* //Forward/Backward Music
                 R.id.musicbackward -> {
                     mplayer.pause()
                     mplayer.seekTo(mplayer.currentPosition - 5000)
                     mplayer.start()
                 }
                 R.id.musicforward -> {
                     mplayer.pause()
                     mplayer.seekTo(mplayer.currentPosition + 5000)
                     mplayer.start()
                 }*/


            }
        }
    }

    override fun onBackPressed() {
        super.onBackPressed()
    }
}

//EDIT As asked the code of the Service:

class MediaPlaybackService: MediaBrowserServiceCompat(){

    //private val MY_MEDIA_ROOT_ID  = "MediaStore.Audio.Media.EXTERNAL_CONTENT_URI"
    private val MY_MEDIA_ROOT_ID ="root"

    private lateinit var mMediaSession: MediaSessionCompat
    private lateinit var mStateBuilder : PlaybackStateCompat.Builder
    private lateinit var  MySessionCallback : MediaSessionCompat.Callback
    private lateinit var mMediaPlayer: MediaPlayer
    override fun onCreate() {
        super.onCreate()
        initMediaPlayer()
        //Create the MediaSession
        mMediaSession = MediaSessionCompat(this,"PLAYER")

        //Setting the necessary Flags (Media Buttons)
        mMediaSession.setFlags(
                MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
        //Set an inital PlaybackStatewith ACTION_BUTTONS, so Media  buttons can start the player
        mStateBuilder = PlaybackStateCompat.Builder().setActions(
                PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PAUSE)
        //Set our PLaybackState for the MediaSession
        mMediaSession.setPlaybackState(mStateBuilder.build())
        MySessionCallback = object : MediaSessionCompat.Callback() {

            override fun onPlayFromUri(uri: Uri?, extras: Bundle?) {
                super.onPlayFromUri(uri, extras)
                if(!mMediaPlayer.isPlaying) {

                    mMediaPlayer.setDataSource(application.applicationContext, uri)

                    mMediaPlayer.setOnPreparedListener {
                        mMediaPlayer.start()
                        Log.i("DURATION",mMediaPlayer.duration.toString())
                    }
                    mMediaPlayer.prepareAsync()

                }


            }


            override fun onPause() {
                super.onPause()
                if(mMediaPlayer.isPlaying){
                mMediaPlayer.pause()}

            }

            override fun onPlay() {
                super.onPlay()
                mMediaPlayer.start()
            }




        }

        //Handles callbacks from Media Controller MySessionCalback is a PlaeHolder
        mMediaSession.setCallback(MySessionCallback)


        mMediaSession.isActive = true


        //Set SessionToken so Activites can communicate with it

        setSessionToken(mMediaSession.getSessionToken());



    }

    override fun onLoadChildren(parentId: String, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
        var mediaItems:ArrayList<MediaBrowserCompat.MediaItem> = ArrayList()

        /*
        var albums = MusicLibrary(this@MediaPlaybackService).getMusic()
        for(item in MusicLibrary.MusicFiles){
            val songList = MediaBrowserCompat.MediaItem(item,
                    MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
            mediaItems.add(songList)
        }


        Log.i("MEDIA_ITEMS",mediaItems.toString())
        */
        result.sendResult(mediaItems)


    }

    override fun onGetRoot(clientPackageName: String, clientUid: Int, rootHints: Bundle?): BrowserRoot? {

        return BrowserRoot(MY_MEDIA_ROOT_ID,null)

        }
    fun initMediaPlayer(){
        mMediaPlayer = MediaPlayer()


    }


}
DrDeep
  • 45
  • 1
  • 9

1 Answers1

1

The problem you are seeing is due to the fact that the calls to connect and disconnect are asynchronous. When you call connect(), the connection is not made immediately. You are just "requesting a connection". You have a connection ONLY after the callback onConnected() is called.

So when the user goes BACK from another Activity, first you call connect() in onStart() and almost immedately onResume() is called. In onResume() when you call isConnected(), this will return false because the connection has not yet been established (ie: the callback onConnected() has not yet been called). You then call connect() which fails with the given exception. You can even see from the exception message:

connect() called while not disconnected (state=CONNECT_STATE_CONNECTING)

That the current state is not "CONNECTED" but "CONNECTING".

You need to wait for the asynchronous callbacks to arrive before you interact with the MediaBrowser.

David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • Ok. So far so good. But my problem still remains, that is always crates a new object of My MediaBrowserService. How can i reconnect to the preexisting Servicec? – DrDeep Sep 04 '18 at 19:08
  • Please post the code from your `MediaBrowserService` by editing the question and adding the code there. Is it possible that your `Service` is just crashing and that's why Android always creates a new instance when you disconnect and connect? – David Wasser Sep 04 '18 at 19:22
  • I don't see anything obvious in the code. If you scan the logcat (don't filter it) do you see any errors or messages regarding the `Service`? – David Wasser Sep 05 '18 at 08:30
  • I'm not that familiar with the workings of `MediaBrowserService`. Why is it a problem if a new instance is created? I assume that Android kills the `Service` when all the clients disconnect. Why would the `Service` need to continue to run if there are no connected clients? You can also see if Android is shutting down the `Service` by overriding `onDestroy()` and calling `super.onDestroy()` and also writing a log message to the logcat. If `onDestroy()` is called, Android is shutting the `Service` down. Otherwise, it is crashing or being killed by Android. – David Wasser Sep 05 '18 at 08:33
  • I cant belive that the Service is killed because the Musik still keeps playing. But i have found an soultion. I create an connectionClass. This class connects instead of the Activity and then sends the mediaController asynchrone to the Activity if its opend – DrDeep Sep 05 '18 at 17:22
  • You wrote that Android creates a new instance of your `MediaBrowserService`. This means that the older instance is dead. Android does not create a new instance of a `Service` if there is already one running. – David Wasser Sep 06 '18 at 07:14
  • Well, i assumed so. But when i reopend it and connect it, the Service doesnt react to button inputs. And the buttons work when I open the Activity for the first time. I think my problem is that my Variable `mMediaBrowser` get lost when i close the activity. – DrDeep Sep 06 '18 at 08:10
  • You call `createMediaBrowser()` in `onCreate()`. When the `Activity` is recreated, it should reconnect and set `mMediaBrowser`. Sounds like you need to do more debugging though. Let me know if you need more assistance. – David Wasser Sep 06 '18 at 09:45
  • Yes it should call it. But as i stated, the buttons doesn't work if i reopen it. I can post you my current solution and you can tell me if its okay or there is a better way. I would be happy about any assitance – DrDeep Sep 06 '18 at 15:59