将服务重新附加到活动?

Posted

技术标签:

【中文标题】将服务重新附加到活动?【英文标题】:Reattach Service to Activity? 【发布时间】:2018-09-03 18:58:21 【问题描述】:

当我第一次打开 Activity 时,它会创建一个 MediaBrowserService。当我尝试重新打开它时,我想重新连接到它。我尝试了多种方法:

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

当我尝试这样做时,它总是会带来错误消息:

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

即使我调用了onStop() 函数mMediaBrowser.disconnect(),这怎么可能?或者这只是错误的方式?当我通过 BackButton 返回时,这一切都会发生。

我的完整代码:

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 询问服务的代码:

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()


    



【问题讨论】:

【参考方案1】:

您看到的问题是由于连接和断开连接的调用是异步的。当您拨打connect() 时,不会立即建立连接。您只是“请求连接”。只有在回调 onConnected() 被调用后,您才有连接。

因此,当用户从另一个Activity 返回时,首先您在onStart() 中调用connect(),然后几乎立即调用onResume()。在onResume() 中调用isConnected() 时,这将返回false,因为尚未建立连接(即:尚未调用回调onConnected())。然后,您调用 connect() 失败并出现给定的异常。你甚至可以从异常信息中看到:

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

当前状态不是“CONNECTED”而是“CONNECTING”

在与MediaBrowser 交互之前,您需要等待异步回调到达。

【讨论】:

好的。到现在为止还挺好。但是我的问题仍然存在,那就是总是创建一个 My MediaBrowserService 的新对象。如何重新连接到预先存在的 Servicec? 请通过编辑问题并将代码添加到您的MediaBrowserService 发布代码。是否有可能您的 Service 正在崩溃,这就是为什么 android 总是在您断开连接和连接时创建一个新实例? 我在代码中看不到任何明显的东西。如果您扫描 logcat(不要过滤它),您是否看到有关 Service 的任何错误或消息? 我对@9​​87654337@ 的工作方式不是很熟悉。如果创建了新实例,为什么会出现问题?我假设当所有客户端断开连接时,Android 会终止 Service。如果没有连接的客户端,为什么Service 需要继续运行?您还可以通过覆盖onDestroy() 并调用super.onDestroy() 以及将日志消息写入logcat 来查看Android 是否正在关闭Service。如果调用onDestroy(),Android 将关闭Service。否则,它正在崩溃或被 Android 杀死。 我无法相信该服务已被杀死,因为音乐仍在继续播放。但我找到了灵魂。我创建了一个连接类。此类连接而不是 Activity,然后将 mediaController asynchrone 发送到 Activity(如果它已打开)

以上是关于将服务重新附加到活动?的主要内容,如果未能解决你的问题,请参考以下文章

活动重新创建意图附加内容为空

重新附加丢失进程的 X 服务器视图

分离和附加数据库

片段未附加到我的活动

当您分离然后重新附加项目集合时,TFS 2015 报告服务器会发生啥情况?

如何在单击的菜单项上将活动类附加到现有类?