首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >刷新MediaBrowserService子描述内容

刷新MediaBrowserService子描述内容
EN

Stack Overflow用户
提问于 2017-10-04 14:51:52
回答 3查看 3.1K关注 0票数 5

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

我想实现以下功能:

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

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

代码语言:javascript
复制
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)

我调用以下方法连接和断开媒体浏览器(同样,在第一个连接上一切运行顺利,但在第二个连接上,我不确定如何通过订阅刷新内容):

代码语言:javascript
复制
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(),这样即使活动保持在后台堆栈上,也可以重新创建订阅。

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

活动

代码语言:javascript
复制
if (mMediaBrowser.isConnected)
        mMediaController?.sendCommand(MediaService.Companion.CustomCommand.REFRESH.toString(), null, null)

服务

代码语言:javascript
复制
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)
            }
        }
    }}

其他研究:

我已经参考过Github上的Google样本代码,以及.

在创建了媒体浏览器服务之后,上述两个回复似乎都没有处理刷新内容的问题,而且该活动至少订阅了一次--我希望避免重新启动该服务,以便音乐可以在后台继续播放。

可能涉及的相关问题:

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-10-09 10:17:50

我的问题与MediaBrowserServiceCompat类无关。之所以会出现这个问题,是因为我调用result.detach()是为了实现一些异步数据获取,而且我使用的侦听器具有来自onLoadChildren方法的parentIdresult变量,并传入并分配了最终val,而不是var

我仍然不完全理解为什么会发生这种情况,这是否是在另一个异步网络调用侦听器中使用Player.EventListener的结果,但解决方案是创建和赋值一个变量(也许还有其他人可以解释这种现象):

代码语言:javascript
复制
// 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
                            }
                        }
                        ...
                     }
                }
       }

    }
票数 0
EN

Stack Overflow用户

发布于 2019-05-10 07:51:49

调用您的音乐服务实现notifyChildrenChanged(String parentId)将触发onLoadChildren,在那里,您可以使用result.sendResult()发送不同的结果。

我所做的就是在我的音乐服务中添加了一个BroadcastReceiver,在它里面,我刚刚调用了notifyChildrenChanged(String parentId)。在我的活动中,当我改变音乐列表时,我发送了一个广播。

票数 3
EN

Stack Overflow用户

发布于 2021-02-11 10:43:14

可选(不推荐)快速修复

MusicService ->

代码语言:javascript
复制
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)
    }
}

ViewModel ->

代码语言:javascript
复制
fun fetchSongs(){
    MusicService.musicServiceInstance?.let{
      it.fetchSongs(params)
     }
}

任择(建议)

MusicPlaybackPreparer

代码语言:javascript
复制
class MusicPlaybackPreparer (
private val firebaseMusicSource: FirebaseMusicSource,
private val serviceScope: CoroutineScope,
private val exoPlayer: SimpleExoPlayer,
private val playerPrepared: (MediaMetadataCompat?) -> Unit

):MediaSessionConnector.PlaybackPreparer {

代码语言:javascript
复制
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

}

MusicServiceConnection

代码语言:javascript
复制
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
}

ViewModel

代码语言:javascript
复制
 fun fetchSongs(){
    val args = Bundle()
    args.putInt("nRecNo", 2)
    musicServiceConnection.sendCommand("Add Songs", args )
}

MusicService ->

代码语言:javascript
复制
 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()
            }
        }
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46567945

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档