MediaSession学习总结

news2024/11/24 21:17:09

1.框架预览

2.用法

2.1参考链接:

MediaSession 简单使用

2.2 服务端要实现MediaBrowserService;

主要实现的功能:

   mPlaybackState = new PlaybackStateCompat.Builder()
                .setState(PlaybackStateCompat.STATE_NONE, currentPostion, 1.0f)
                .setActions(getAvailableActions(PlaybackStateCompat.STATE_NONE))
                .build();
 
        //初始化,第一个参数为context,第二个参数为String类型tag,这里就设置为类名了
        mediaSession = new MediaSessionCompat(this, "MediaService");
        //设置token
        setSessionToken(mediaSession.getSessionToken());
        //设置callback,这里的callback就是客户端对服务指令到达处
        mediaSession.setCallback(mCallback);

在MediaSessionCompat.Callback 中实现具体的播放与控制逻辑

  //mediaSession设置的callback,也是客户端控制指令所到达处
    private final MediaSessionCompat.Callback mCallback = new MediaSessionCompat.Callback() {
        //重写的方法都是选择性重写的,不完全列列举,具体可以查询文章末尾表格
        @Override
        public void onPlay() {
            super.onPlay();
            Log.d(TAG, "onPlay: isPrepare = " + isPrepare);
            //客户端mMediaController.getTransportControls().play()就会调用到这里,以下类推
            //处理播放逻辑
            //处理完成后通知客户端更新,这里就会回调给客户端的MediaController.Callback
            if (null != mMediaPlayer && !isPrepare) {
                handleOpenUri(MusicListData.rawToUri(MediaService.this, Objects.requireNonNull(getPlayBean()).mediaId));
            } else {
                handlePlay();
            }
        }
 
        @Override
        public void onPause() {
            super.onPause();
            Log.d(TAG, "onPause: ");
            handlePause(true);
        }
 
        @Override
        public void onSeekTo(long pos) {
            super.onSeekTo(pos);
            //设置到指定进度时触发
        }
 
        @Override
        public void onSkipToPrevious() {
            int pos = (currentPostion + mPlayBeanList.size() - 1) % mPlayBeanList.size();
            Log.e(TAG, "onSkipToPrevious  pos = " + pos);
            handleOpenUri(MusicListData.rawToUri(MediaService.this, Objects.requireNonNull(setPlayPosition(pos)).mediaId));
        }
 
        @Override
        public void onSkipToNext() {
            super.onSkipToNext();
 
            //下一首
            //通知媒体信息改变
//            mediaSession.setMetadata(mediaMetadata);
            int pos = (currentPostion + 1) % mPlayBeanList.size();
            Log.d(TAG, "onSkipToNext: pos = " + pos);
            handleOpenUri(MusicListData.rawToUri(MediaService.this, Objects.requireNonNull(setPlayPosition(pos)).mediaId));
        }
 
        /**
         * 响应MediaControllerCompat.getTransportControls().playFromUri
         *
         * @param uri uri
         * @param extras extras
         */
        @Override
        public void onPlayFromUri(Uri uri, Bundle extras) {
            Log.e(TAG, "onPlayFromUri");
            int position = extras.getInt("playPosition");
            setPlayPosition(position);
            handleOpenUri(uri);
        }
 
        @Override
        public void onCustomAction(String action, Bundle extras) {
            super.onCustomAction(action, extras);
            //自定义指令发送到的地方
            //对应客户端 mMediaController.getTransportControls().sendCustomAction(...)
        }
 
    };

这里要注意监听音频焦点的变化。

  AudioManager.OnAudioFocusChangeListener mOnAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
        @Override
        public void onAudioFocusChange(int focusChange) {
            Log.d(TAG, "onAudioFocusChange  focusChange = " + focusChange + ", before isHaveAudioFocus = " +
                    isHaveAudioFocus);
            switch (focusChange) {
                case AudioManager.AUDIOFOCUS_LOSS:
                    // 音源丢失
                    isHaveAudioFocus = false;
                    mCallback.onPause();
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    // 音源短暂丢失
                    isHaveAudioFocus = false;
                    Log.d(TAG, " AUDIOFOCUS_LOSS_TRANSIENT  ");
                    handlePause(false);
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    //  降低音量
                    break;
                case AudioManager.AUDIOFOCUS_GAIN:
                    // 获得音源
                    isHaveAudioFocus = true;
                    mCallback.onPlay();
                    break;
                case AudioManager.AUDIOFOCUS_REQUEST_FAILED:
                    // 音源申请失败
                    break;
                default:
                    break;
            }
        }
    };
 



    private int requestAudioFocus() {
        int result = mAudioManager.requestAudioFocus(mOnAudioFocusChangeListener, AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN);
        isHaveAudioFocus = AudioManager.AUDIOFOCUS_REQUEST_GRANTED == result;
        return result;
    }
 
 
    /**
     * 释放焦点
     */
    private void abandAudioFocus() {
        int result = mAudioManager.abandonAudioFocus(mOnAudioFocusChangeListener);
        isHaveAudioFocus = AudioManager.AUDIOFOCUS_REQUEST_GRANTED == result;
    }

播放的同时需要把播放状态和歌曲信息 回传给客户端

   private void sendPlaybackState(int state, Bundle extras) {
        mPlaybackState = new PlaybackStateCompat.Builder()
                .setState(state, currentPostion, 1.0f)
                .setActions(getAvailableActions(state))
                .setExtras(extras)
                .build();
        mediaSession.setPlaybackState(mPlaybackState);
    }

歌曲数据需要转成对应格式

private MediaMetadataCompat buildFromLocal(Song song){
        String title = song.getTitle();
        String album = song.getAlbum();
        String artist = song.getArtist();
        int duration = song.getDuration();
        String source = song.getPath();
        String strId = "";
        if(title != null && artist != null){
            strId = title + artist;
        }
        String id = String.valueOf(strId.hashCode());
        String albumArt = song.getAlbumObj().getAlbumArt();
        return new MediaMetadataCompat.Builder()
                .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID,id)
                .putString(SongSource.CUSTOM_METADATA_TRACK_SOURCE, source)
                .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI,source)
                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM,album)
                .putString(MediaMetadataCompat.METADATA_KEY_ARTIST,artist)
                .putString(MediaMetadataCompat.METADATA_KEY_ART_URI,albumArt)
                .putLong(MediaMetadataCompat.METADATA_KEY_DURATION,duration)
                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
                .build();
    }

2.3客户端绑定MediaBrowserService.

创建MediaBrowser 客户端,连接倒对应服务器。

private MediaBrowserCompat mMediaBrowser;

mMediaBrowser = new MediaBrowserCompat(this,
                new ComponentName(this, MusicService.class), mConnectionCallback, null);



 private final MediaBrowserCompat.ConnectionCallback mConnectionCallback =
            new MediaBrowserCompat.ConnectionCallback() {
                @Override
                public void onConnected() {
                    //说明已经连接上了
                    try {
                        connectToSession(mMediaBrowser.getSessionToken());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            };

连接的callback,在成功连接时,获取 MediaController, 并注册MediaControllerCompat.Callback。

 private final MediaBrowserCompat.ConnectionCallback mConnectionCallbacks = new MediaBrowserCompat.ConnectionCallback() {
 
        @Override
        public void onConnected() {
            Log.d(TAG, "MediaBrowser.onConnected");
            if (mMediaBrowser.isConnected()) {
                String mediaId = mMediaBrowser.getRoot();
                mMediaBrowser.unsubscribe(mediaId);
                //之前说到订阅的方法还需要一个参数,即设置订阅回调SubscriptionCallback
                //当Service获取数据后会将数据发送回来,此时会触发SubscriptionCallback.onChildrenLoaded回调
                mMediaBrowser.subscribe(mediaId, browserSubscriptionCallback);
                try {
                    MediaControllerCompat mediaController = new MediaControllerCompat(DemoActivity.this,
                            mMediaBrowser.getSessionToken());
 
                    MediaControllerCompat.setMediaController(DemoActivity.this, mediaController);
 
//                    mediaController = new MediaControllerCompat(DemoActivity.this, mMediaBrowser.getSessionToken());
                    mediaController.registerCallback(mMediaControllerCallback);
                    if (mediaController.getMetadata() != null) {
                        updatePlayMetadata(mediaController.getMetadata());
                        updatePlayState(mediaController.getPlaybackState());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
 
        @Override
        public void onConnectionSuspended() {
            // 连接中断回调
            Log.d(TAG, "onConnectionSuspended");
        }
 
        @Override
        public void onConnectionFailed() {
            Log.d(TAG, "onConnectionFailed");
        }
    };

注册回调,监听播放状态和歌曲信息的变化。

private final MediaControllerCompat.Callback mMediaControllerCallback =
            new MediaControllerCompat.Callback() {

                @Override
                public void onPlaybackStateChanged(@NonNull PlaybackStateCompat state) {
                    //这里根据播放状态的改变,本地ui做相应的改变,例如播放模式,播放、暂停,进度条等
                    updatePlaybackState(state);
                }

                @Override
                public void onMetadataChanged(MediaMetadataCompat metadata) {
                    //歌曲的信息,例如播放时长,歌曲名称等
                    updateDuration(metadata);
                }

@override
 public void onQueueChanged(List<QueueItem> queue) {
        }
            };

控制服务端的播放逻辑。通过mediaController.getTransportControls() 去调用。如:

TransportControls.skipToPrevious();
TransportControls.skipToNext();
TransportControls.pause();
TransportControls.play();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2244983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习大数据DAY61 宽表加工

目录 模型设计 加工宽表 任务调度&#xff1a; 大表 - 把很多数据整合起来 方便后续的明细查询和指标计算 模型设计 设计 建模 设计: excel 文档去编写 建模: 使用建模工具 PowerDesigner Navicat 在线画图工具... 把表结构给绘 制出来 共享\项目课工具\pd 加工宽表 数…

零基础入门Flink,掌握基本使用方法

Flink基本概念 首先来讲&#xff0c;Flink是一个面向数据流处理和批处理的分布式开源计算框架。 那么&#xff0c;流处理和批处理分别处理什么样的数据呢&#xff0c;这就涉及两个概念-无界流和有界流 无界流VS有界流 任何类型的数据都可以形成流数据&#xff0c;比如用户…

Linux设置以及软件的安装(hadoop集群安装02)

一、Linux的常见设置 1、设置静态IP vi /etc/sysconfig/network-scripts/ifcfg-ens33 如何查看自己的虚拟机的网关&#xff1a; 完整的配置&#xff08;不要拷贝我的&#xff09;&#xff1a; TYPE"Ethernet" PROXY_METHOD"none" BROWSER_ONLY"no&…

数据中台方法论:数据汇聚

文章目录 一、数据汇聚概述二、 汇聚数据类型2.1 结构化数据2.2 半结构化数据2.3 非结构化数据 三、汇聚数据模式四、汇聚数据方法四、数据汇聚工具五、数据汇聚使用经验 数据小伙伴们&#xff0c;之前咱们长篇大论的聊聊过【数据中台建设方法论从0到1】&#xff0c;从数据中台…

【Maven】nexus 配置私有仓库配置【转】

介绍&#xff1a;【Maven】Nexus几个仓库的介绍-CSDN博客 一、仓库类型 proxy 远程仓库的代理&#xff0c;比如说nexus配置了一个central repository的proxy,当用户向这个proxy请求一个artifact的时候&#xff0c;会现在本地查找&#xff0c;如果找不到&#xff0c;则会从远程…

3C产品说明书电子化转变:用户体验、环保与商业机遇的共赢

在科技日新月异的当代社会&#xff0c;3C产品&#xff08;涵盖计算机类、通信类和消费类电子产品&#xff09;已成为我们日常生活中不可或缺的重要元素。与此同时&#xff0c;这些产品的配套说明书也经历了一场从纸质到电子化的深刻变革。这一转变不仅体现了技术的飞速进步&…

【YOLOv8】安卓端部署-2-项目实战

文章目录 1 准备Android项目文件1.1 解压文件1.2 放置ncnn模型文件1.3 放置ncnn和opencv的android文件1.4 修改CMakeLists.txt文件 2 手机连接电脑并编译软件2.1 编译软件2.2 更新配置及布局2.3 编译2.4 连接手机 3 自己数据集训练模型的部署4 参考 1 准备Android项目文件 1.1…

虚拟网卡驱动和DM9000C移植

网卡驱动程序框架 网卡驱动程序“收发功能”&#xff1a; 只要把上层的数据发给网卡&#xff0c;从网卡来的数据构造成包给上层即可。网卡只需要 “socket”编程&#xff0c;不需要打开某设备。 驱动程序都是以面向对象的思想写的&#xff0c;都有相关的结构体。 编程步骤 …

Vue3 + Vite 项目引入 Typescript

文章目录 一、TypeScript简介二、TypeScript 开发环境搭建三、编译方式1. 自动编译单个文件2. 自动编译整个项目 四、配置文件1. compilerOptions基本选项严格模式相关选项&#xff08;启用 strict 后自动包含这些&#xff09;模块与导入相关选项 2. include 和 excludeinclude…

Cyberchef使用功能之-多种压缩/解压缩操作对比

cyberchef的compression操作大类中有大量的压缩和解压缩操作&#xff0c;每种操作的功能和区别是什么&#xff0c;本章将进行讲解&#xff0c;作为我的专栏《Cyberchef 从入门到精通教程》中的一篇&#xff0c;详见这里。 关于文件格式和压缩算法的理论部分在之前的文章《压缩…

Istio分布式链路监控搭建:Jaeger与Zipkin

分布式追踪定义 分布式追踪是一种用来跟踪分布式系统中请求的方法&#xff0c;它可以帮助用户更好地理解、控制和优化分布式系统。分布式追踪中用到了两个概念&#xff1a;TraceID 和 SpanID。 TraceID 是一个全局唯一的 ID&#xff0c;用来标识一个请求的追踪信息。一个请求…

Linux修改/etc/hosts不起作用(ping: xxx: Name or service not known)的解决方法——开启NSCD

​ 问题描述 起因是我在实验室云资源池的一台虚拟机&#xff08;CentOS 8.5&#xff09;上的/etc/hosts文件中为Fabric网络节点的域名指定了IP&#xff1a; IP可以ping通&#xff0c;但是ping域名时提示ping: xxx: Name or service not known。 问题本身应该是Linux通用的&a…

Python中Tushare(金融数据库)入门详解

文章目录 Python中Tushare&#xff08;金融数据库&#xff09;入门详解一、引言二、安装与注册1、安装Tushare2、注册与获取Token 三、Tushare基本使用1、设置Token2、获取数据2.1、获取股票基础信息2.2、获取交易日历2.3、获取A股日线行情2.4、获取沪股通和深股通成份股2.5、获…

【网络】网络抓包与协议分析

网络抓包与协议分析 一. 以太网帧格式分析 这是以太网数据帧的基本格式&#xff0c;包含目的地址(6 Byte)、源地址(6 Byte)、类型(2 Byte)、数据(46~1500 Byte)、FCS(4 Byte)。 Mac 地址类型 分为单播地址、组播地址、广播地址。 单播地址&#xff1a;是指第一个字节的最低位…

RabbitMQ的工作队列在Spring Boot中实现(详解常⽤的⼯作模式)

上文着重介绍RabbitMQ 七种工作模式介绍RabbitMQ 七种工作模式介绍_rabbitmq 工作模式-CSDN博客 本篇讲解如何在Spring环境下进⾏RabbitMQ的开发.&#xff08;只演⽰部分常⽤的⼯作模式&#xff09; 目录 引⼊依赖 一.工作队列模式 二.Publish/Subscribe(发布订阅模式) …

python学习_3.正则表达式

来源:B站/麦叔编程 1. 正则表达式的7个境界 假设有一段文字&#xff1a; text 身高:178&#xff0c;体重&#xff1a;168&#xff0c;学号&#xff1a;123456&#xff0c;密码:9527要确定文本中是否包含数字123456&#xff0c;我们可以用in运算符&#xff0c;也可以使用inde…

Python学习------第十天

数据容器-----元组 定义格式&#xff0c;特点&#xff0c;相关操作 元组一旦定义&#xff0c;就无法修改 元组内只有一个数据&#xff0c;后面必须加逗号 """ #元组 (1,"hello",True) #定义元组 t1 (1,"hello") t2 () t3 tuple() prin…

nodejs基于微信小程序的云校园的设计与实现

摘 要 相比于传统的校园管理方式&#xff0c;智能化的管理方式可以大幅提高校园的管理效率&#xff0c;实现了云校园管理的标准化、制度化、程序化的管理&#xff0c;有效地防止了云校园信息的不规范管理&#xff0c;提高了信息的处理速度和精确度&#xff0c;能够及时、准确地…

Excel——宏教程(精简版)

一、宏的简介 1、什么是宏&#xff1f; Excel宏是一种自动化工具&#xff0c;它允许用户录制一系列操作并将其转换为VBA(Visual Basic for Applications)代码。这样&#xff0c;用户可以在需要时执行这些操作&#xff0c;以自动化Excel任务。 2、宏的优点 我们可以利用宏来…

绿光一字线激光模组:工业制造与科技创新的得力助手

在现代工业制造和科技创新领域&#xff0c;绿光一字线激光模组以其独特的性能和广泛的应用前景&#xff0c;成为了不可或缺的关键设备。这种激光模组能够发射出一条明亮且精确的绿色激光线&#xff0c;具有高精度、高稳定性和长寿命的特点&#xff0c;为各种精密加工和测量需求…