代码之家  ›  专栏  ›  技术社区  ›  Benjamin Scholtz Sampath Kumar

刷新MediaBrowserService订阅内容

  •  5
  • Benjamin Scholtz Sampath Kumar  · 技术社区  · 7 年前

    我已经成功创建了一个活动,并(通过订阅)将其耦合到媒体浏览器服务。此媒体浏览器服务可以继续运行并在后台播放音乐。我希望能够在某个阶段刷新内容,无论是当应用程序再次出现在前台时,还是在SwipeRefreshLayout事件期间。

    我想实现以下功能:

    1. 启动MediaBrowserServiceCompat服务。
    2. 在应用程序关闭时,允许服务继续运行和播放音乐。
    3. 在稍后的阶段,或在SwipeRefreshLayout事件中,重新连接并订阅该服务以获取新内容。

    我收到的问题是,在MediaBrowserService中(在创建订阅后),您只能从onLoadChildren()方法调用sendResult()一次,因此下次尝试使用相同的根订阅媒体浏览器服务时,当第二次调用sendResult()时,会出现以下异常:

    E/UncaughtException: java.lang.IllegalStateException: sendResult() called when either sendResult() or sendError() had already been called for: MEDIA_ID_ROOT
                                                        at android.support.v4.media.MediaBrowserServiceCompat$Result.sendResult(MediaBrowserServiceCompat.java:602)
                                                        at com.roostermornings.android.service.MediaService.loadChildrenImpl(MediaService.kt:422)
                                                        at com.roostermornings.android.service.MediaService.access$loadChildrenImpl(MediaService.kt:50)
                                                        at com.roostermornings.android.service.MediaService$onLoadChildren$1$onSyncFinished$playerEventListener$1.onPlayerStateChanged(MediaService.kt:376)
                                                        at com.google.android.exoplayer2.ExoPlayerImpl.handleEvent(ExoPlayerImpl.java:422)
                                                        at com.google.android.exoplayer2.ExoPlayerImpl$1.handleMessage(ExoPlayerImpl.java:103)
                                                        at android.os.Handler.dispatchMessage(Handler.java:102)
                                                        at android.os.Looper.loop(Looper.java:150)
                                                        at android.app.ActivityThread.main(ActivityThread.java:5665)
                                                        at java.lang.reflect.Method.invoke(Native Method)
                                                        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:822)
                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:712)
    

    override fun onStart() {
            super.onStart()
    
            mMediaBrowser = MediaBrowserCompat(this, ComponentName(this, MediaService::class.java), connectionCallback, null)
    
            if (!mMediaBrowser.isConnected)
                mMediaBrowser.connect()
    }
    
    override fun onPause() {
            super.onPause()
    
            //Unsubscribe and unregister MediaControllerCompat callbacks
            MediaControllerCompat.getMediaController(this@DiscoverFragmentActivity)?.unregisterCallback(mediaControllerCallback)
            if (mMediaBrowser.isConnected) {
                mMediaBrowser.unsubscribe(mMediaBrowser.root, subscriptionCallback)
                mMediaBrowser.disconnect()
            }
    }
    

    我在onPause()而不是onDestroy()中取消订阅并断开连接,这样即使活动保留在后堆栈中,也会重新创建订阅。

    分别在活动和服务中用于刷卡刷新的实际方法:

    活动

    if (mMediaBrowser.isConnected)
            mMediaController?.sendCommand(MediaService.Companion.CustomCommand.REFRESH.toString(), null, null)
    

    服务

    inner class MediaPlaybackPreparer : MediaSessionConnector.PlaybackPreparer {
    
        ...
    
        override fun onCommand(command: String?, extras: Bundle?, cb: ResultReceiver?) {
            when(command) {
                // Refresh media browser content and send result to subscribers
                CustomCommand.REFRESH.toString() -> {
                    notifyChildrenChanged(MEDIA_ID_ROOT)
                }
            }
        }}
    

    上述回购似乎都无法解决 令人耳目一新的

    可能的相关问题:

    3 回复  |  直到 7 年前
        1
  •  3
  •   Hampel Előd    6 年前

    呼叫您音乐服务实施 notifyChildrenChanged(String parentId) onLoadChildren 在里面,你可以用 result.sendResult() .

    我所做的是添加了一个 BroadcastReceiver 到我的音乐服务和里面,我只是打电话给 notifyChildrenChanged(字符串parentId)

        2
  •  1
  •   Mohd Danish    4 年前

    companion object {
        var musicServiceInstance:MusicService?=null
    }
    
    override fun onCreate() {
        super.onCreate()
        musicServiceInstance=this
    }
    
    //api call
    fun fetchSongs(params:Int){
        serviceScope.launch {
            firebaseMusicSource.fetchMediaData(params)
    
            //Edit Data or Change Data
             notifyChildrenChanged(MEDIA_ROOT_ID)
        }
    }
    

    fun fetchSongs(){
        MusicService.musicServiceInstance?.let{
          it.fetchSongs(params)
         }
    }
    

    可选(推荐)

    class MusicPlaybackPreparer (
    private val firebaseMusicSource: FirebaseMusicSource,
    private val serviceScope: CoroutineScope,
    private val exoPlayer: SimpleExoPlayer,
    private val playerPrepared: (MediaMetadataCompat?) -> Unit
    

    ):MediaSessionConnector。回放准备程序{

    override fun onCommand(player: Player, controlDispatcher: ControlDispatcher, command: String, extras: Bundle?, cb: ResultReceiver?
    ): Boolean {
        when(command){
             //edit data or fetch more data from api
            "Add Songs"->{
                serviceScope.launch {
                    firebaseMusicSource.fetchMediaData()
                }
             }
           
        }
        return false
    }
    
    
    override fun getSupportedPrepareActions(): Long {
        return PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID or
                PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID
    }
    
    override fun onPrepare(playWhenReady: Boolean) = Unit
    
    override fun onPrepareFromMediaId(mediaId: String, playWhenReady: Boolean, extras: Bundle?) {
        firebaseMusicSource.whenReady {
            val itemToPlay = firebaseMusicSource.songs.find { mediaId == it.description.mediaId }
            playerPrepared(itemToPlay)
        }
    }
    
    override fun onPrepareFromSearch(query: String, playWhenReady: Boolean, extras: Bundle?) = Unit
    
    override fun onPrepareFromUri(uri: Uri, playWhenReady: Boolean, extras: Bundle?) = Unit
    

    音乐服务连接

    fun sendCommand(command: String, parameters: Bundle?) =
        sendCommand(command, parameters) { _, _ -> }
    
    private fun sendCommand(
        command: String,
        parameters: Bundle?,
        resultCallback: ((Int, Bundle?) -> Unit)
    ) = if (mediaBrowser.isConnected) {
        mediaController.sendCommand(command, parameters, object : ResultReceiver(Handler()) {
            override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
                resultCallback(resultCode, resultData)
            }
        })
        true
    } else {
        false
    }
    

    视图模型

     fun fetchSongs(){
        val args = Bundle()
        args.putInt("nRecNo", 2)
        musicServiceConnection.sendCommand("Add Songs", args )
    }
    

     override fun onLoadChildren(
        parentId: String,
        result: Result<MutableList<MediaBrowserCompat.MediaItem>>
    ) {
        when(parentId) {
            MEDIA_ROOT_ID -> {
                val resultsSent = firebaseMusicSource.whenReady { isInitialized ->
                    if(isInitialized) {
                        try {
                            result.sendResult(firebaseMusicSource.asMediaItems())
                            if(!isPlayerInitialized && firebaseMusicSource.songs.isNotEmpty()) {
                                preparePlayer(firebaseMusicSource.songs, firebaseMusicSource.songs[0], true)
                                isPlayerInitialized = true
                            }
                        }
                       catch (exception: Exception){
                           // not recommend to notify here , instead notify when you 
                           // change existing list in MusicPlaybackPreparer onCommand()
                           notifyChildrenChanged(MEDIA_ROOT_ID)
                       }
                    } else {
                        result.sendResult(null)
                    }
                }
                if(!resultsSent) {
                    result.detach()
                }
            }
        }
    }
    
        3
  •  0
  •   Benjamin Scholtz Sampath Kumar    7 年前

    我的问题与MediaBrowserServiceCompat类无关。问题是因为我打电话来的 result.detach() 为了实现一些异步数据获取,我使用的侦听器同时具有 parentId result val var

    我仍然不完全理解为什么会发生这种情况,是否是使用 Player.EventListener

    // Create variable
    var currentResult: Result<List<MediaBrowserCompat.MediaItem>>? = null
    
    override fun onLoadChildren(parentId: String, result: MediaBrowserServiceCompat.Result<List<MediaBrowserCompat.MediaItem>>) {
        // Use result.detach to allow calling result.sendResult from another thread
        result.detach()
        // Assign returned result to temporary variable
        currentResult = result
        currentParentId = parentId
    
        // Create listener for network call
        ChannelManager.onFlagChannelManagerDataListener = object : ChannelManager.Companion.OnFlagChannelManagerDataListener {
           override fun onSyncFinished() {
                // Create a listener to determine when player is prepared
                val playerEventListener = object : Player.EventListener {
    
                    override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
                         when(playbackState) {
                            Player.STATE_READY -> {
                                if(mPlayerPreparing) {
                                    // Prepare content to send to subscribed content
                                    loadChildrenImpl(currentParentId, currentResult as MediaBrowserServiceCompat.Result<List<MediaBrowserCompat.MediaItem>>)
                                    mPlayerPreparing = false
                                }
                            }
                            ...
                         }
                    }
           }
    
        }