Jetpack - Media3(MediaController 响应外部控制)

一、概念

二、提供服务

2.1 创建 Service

MediaSession.Callback

可重写的回调

onAddMediaItems()
onConnect()

onCustomCommand()

控制器发送过来的自定义命令。

onDisconnected()
onMediaButtonEvent()
onPlaybackResumption()
onPlayerInteractionFinished()
onPostConnect()
onSetMediaItems()
onSetRating()
class PlayerService : MediaSessionService() {
    private var mediaSession: MediaSession? = null
    private val sessionCallback by lazy {
        //重写需要的回调来响应外部控制器发送的命令
        object : MediaSession.Callback {}
    }

    override fun onCreate() {
        super.onCreate()
        //不建议在这里设置播放器监听(Player.Listener)
        //因为可能有很多MediaController连接过来
        //所以应该放在初始化MediaController的时候定制各自的监听
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player)
            .setCallback(sessionCallback)    //设置会话监听
            .build()
    }

    // 供外部控制器访问
    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
         return mediaSession
    }

    // 用户关闭应用时停止服务
    override fun onTaskRemoved(rootIntent: Intent?) {
        pauseAllPlayersAndStopSelf()
    }

    // 释放资源
    override fun onDestroy() {
        super.onDestroy()
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
    }
}

2.2 注册 Service

<service
    android:name=".PlayerService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

2.3 配置权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

三、使用

3.1 连接服务

class ControllerActivity : AppCompatActivity() {
    private lateinit var controllerFuture: ListenableFuture<MediaController>
    private var mediaController: MediaController? = null
    private val controllerListener by lazy {
        //重写需要的控制器监听
        object : MediaController.Listener {}
    }
    private val playerListener by lazy {
        //重写需要的播放器监听
        object : Player.Listener {}
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_controller)
        startForegroundService(Intent(this, PlayerService::class.java))
        connectToSession()
    }

    private fun connectToSession() {
        // 指定要连接的Service
        val sessionToken = SessionToken(this, ComponentName(this, PlayerService::class.java))
        // 异步构建 MediaController
        controllerFuture = MediaController.Builder(this, sessionToken)
            .setListener(controllerListener)    //控制器监听
            .buildAsync()
        // 连接成功的回调
        controllerFuture.addListener({
            try {
                mediaController = controllerFuture.get().apply{
                    // 进行初始化配置(用起来同ExoPlayer的Player一样)
                    addListener(playerListener)    //播放器监听
                    repeatMode = Player.REPEAT_MODE_ONE
                    prepare()
                }
            } catch (e: Exception) {
                //连接失败
            }
        // 如果在界面以外的其他进程中运行服务,使用 ContextCompat.getMainExecutor() 而不是 MoreExecutors.directExecutor()。
        }, mainExecutor )
    }
}

3.1.1 优化

mediaController 不一定什么时候初始化完毕,若是需要界面一打开就要播放的情况,可以对成员变量使用Delegates在不为默认值null时撤掉遮挡UI允许用户操作。

private var mediaController: MediaController? by Delegates.observable(null) { property, oldValue, newValue ->
    //此时 mediaController 可用,关闭初始化遮挡界面允许用户操作
}

3.2 设置控制器监听(连接监听)

MediaController.Listener

控制器可以设置的监听

onAvailableSessionCommandsChanged()

onCustomCommand()

会话发送过来的自定义命令。

onCustomLayoutChanged()
onDisconnected()
onError()
onExtrasChanged()
onMediaButtonPreferencesChanged()
onSessionActivityChanged()
onSetCustomLayout()
private val controllerListener by lazy {
    object : MediaController.Listener {
        // 当会话发送自定义命令时
        override fun onCustomCommand(controller: MediaController, command: SessionCommand, args: Bundle): ListenableFuture<SessionResult> {
            return when (command.customAction) {
//                    "FAVORITE" -> {
//                        val isFavorite = args.getBoolean("isFavorite", false)
//                        handleFavoriteCommand(isFavorite)
//                        Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
//                    }
                else -> super.onCustomCommand(controller, command, args)
            }
        }
        // 当控制器与会话断开连接时
        override fun onDisconnected(controller: MediaController) {
            super.onDisconnected(controller)
        }
    }
}

3.3 设置播放器监听

同 ExoPlayer,都是 Player.Listener。

private val playerListener by lazy {
    object : Player.Listener {
        //播放器状态
        override fun onPlaybackStateChanged(playbackState: Int) {
            when (playbackState) {
                //空闲
                Player.STATE_IDLE -> {}
                //缓冲中
                Player.STATE_BUFFERING -> {}
                //就绪
                Player.STATE_READY -> {}
                //播放完
                Player.STATE_ENDED -> {}
            }
        }
        //播放列表
        override fun onTimelineChanged(timeline: Timeline, reason: Int) {
            when(reason) {
                //播放列表发生变化时
                Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED -> {}
                //媒体源更新而发生变化
                Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE -> {}
            }
        }
        //播放错误
        override fun onPlayerError(error: PlaybackException) {
            when (error.cause) {
                //http错误
                is HttpDataSource.HttpDataSourceException -> {}
                else -> {}
            }
            when (error.errorCode) {
                //网络错误
                PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED -> {}
                //解码错误
                PlaybackException.ERROR_CODE_DECODING_FAILED -> {}
                else -> {}
            }
        }
        //播放暂停按钮
        override fun onIsPlayingChanged(isPlaying: Boolean) {}
        //切歌
        override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {}
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值