网上蓝牙音乐相关的文章实在太少,贡献一下自己的微薄之力

先讲一些零碎知识点:

##################################华丽分割线###################################
蓝牙的源码路径

frameworks\base\core\java\android\bluetooth

##################################华丽分割线###################################
蓝牙音乐使用中需要用到的权限

在apk中的AndroidManifest.xml中要有以下语句获得蓝牙相关权限:

  <uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />

##################################华丽分割线 下面介绍蓝牙的广播部分 start###############################

蓝牙的广播部分

蓝牙的连接

注册蓝牙回调,这里需要讲一下BluetoothAdapter、BluetoothAvrcpController、BluetoothA2dpSink三个类

BluetoothAdapter作用:

获取蓝牙开关状态,搜索蓝牙,配对蓝牙等

BluetoothAvrcpController作用:

这个类里主要是维护蓝牙音乐的相关信息更新(ID3),操作控制蓝牙音乐(播放暂停上一曲下一曲等)

BluetoothA2dpSink 作用:

这个类里主要是确定蓝牙音乐是否连接上

注册蓝牙回调广播

    public void registerBtReceiver(Context context) {IntentFilter intentFilter = new IntentFilter();//A2DP连接状态改变intentFilter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);//A2DP播放状态改变intentFilter.addAction(BluetoothA2dpSink.ACTION_PLAYING_STATE_CHANGED);//监听蓝牙音乐暂停、播放等 intentFilter.addAction(BluetoothAvrcpController.ACTION_TRACK_EVENT);//连接状态intentFilter.addAction(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED);//浏览        intentFilter.addAction(BluetoothAvrcpController.ACTION_BROWSE_CONNECTION_STATE_CHANGED);// 正在浏览的事件 intentFilter.addAction(BluetoothAvrcpController.ACTION_BROWSING_EVENT);//当前 媒体 项目 改变  intentFilter.addAction(BluetoothAvrcpController.ACTION_CURRENT_MEDIA_ITEM_CHANGED);intentFilter.addAction(BluetoothAvrcpController.ACTION_PLAYER_SETTING);//没有媒体信息intentFilter.addAction(BluetoothAvrcpController.ACTION_PLAY_FAILURE);intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);intentFilter.addAction(BluetoothDevice.ACTION_NAME_CHANGED);context.registerReceiver(mBtReceiver, intentFilter);}

注册完回调以后,会有一个回调函数

    private BroadcastReceiver mBtReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {switch (intent.getAction()) {case BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED://todo 主要处理蓝牙a2dp连接状态break;case BluetoothA2dpSink.ACTION_PLAYING_STATE_CHANGED:LogUtil.e(TAG, "mBtReceiver,//控制蓝牙的播放状态,启动这个作为播放状态更新,时序太慢,所以注意不要用这个回调更新播放状态,建议在BluetoothAvrcpController.ACTION_TRACK_EVENT回调中处理播放状态break;case BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED:break;case BluetoothAvrcpController.ACTION_TRACK_EVENT://处理媒体信息,包括需要显示的MediaMetadata基本信息,和实时更新的PlaybackState信息break;case BluetoothAvrcpController.ACTION_BROWSE_CONNECTION_STATE_CHANGED:// 手机端断开并重新连接上需要更新break;case BluetoothAvrcpController.ACTION_BROWSING_EVENT://蓝牙音乐列表break;case BluetoothAvrcpController.ACTION_CURRENT_MEDIA_ITEM_CHANGED://广播得到媒体信息BluetoothAvrcpMediaItemData mMeidaItemData = intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_MEDIA_ITEM_DATA);break;case BluetoothAvrcpController.ACTION_PLAYER_SETTING:break;case BluetoothAvrcpController.ACTION_PLAY_FAILURE://这个是系统增加的接口,用于提示 当手机端播放器没有打开或者没有播放器的时候,是没有蓝牙音乐相关信息的,考虑到有些只是上层应用用原生的蓝牙多说一下,这种接口上层是没有的break;case BluetoothAdapter.ACTION_STATE_CHANGED://蓝牙开关状态 但一般不用这个,而是用 BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED 来判断break;case BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED://用这个广播判断蓝牙连接状态,注意这个是总开关,包含了蓝牙音乐和蓝牙电话break;case BluetoothDevice.ACTION_NAME_CHANGED://检查蓝牙名字,是否更新break;}}};

注册profile回调

  public void registerProfile(Context context) {if (BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, profileServiceListener, BluetoothProfile.A2DP_SINK)) {LogUtil.i(TAG, "registerProfile: A2DP_SINK success");} else {LogUtil.e(TAG, "registerProfile: A2DP_SINK failed");}if (BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, profileServiceListener, BluetoothProfile.AVRCP_CONTROLLER)) {LogUtil.i(TAG, "registerProfile: AVRCP_CONTROLLER success");} else {LogUtil.e(TAG, "registerProfile: AVRCP_CONTROLLER failed");}}

A2dp和Avrcp的监听,主要是处理一些,应用还未起来,蓝牙已经连接上了,有些广播不走,需要通过这里来处理

//这个类里主要是确定蓝牙音乐是否连接上private BluetoothA2dpSink mBluetoothA2dpSink;//这个类里主要是维护蓝牙音乐的相关信息更新(ID3),操作控制蓝牙音乐(播放暂停上一曲下一曲等)private BluetoothAvrcpController mAvrcpController;private BluetoothProfile.ServiceListener profileServiceListener = new BluetoothProfile.ServiceListener() {@Overridepublic void onServiceConnected(int profile, BluetoothProfile proxy) {LogUtil.i(TAG, "onServiceConnected: profile=" + profile + ",BluetoothProfile=" + proxy);switch (profile) {case BluetoothProfile.A2DP_SINK:mBluetoothA2dpSink = (BluetoothA2dpSink) proxy;LogUtil.e(TAG, "onServiceConnected: mBluetoothA2dpSink=" + mBluetoothA2dpSink);//todo 这里可以做设置蓝牙为可用状态,或者更新设备名字,设置音频焦点break;case BluetoothProfile.AVRCP_CONTROLLER:mAvrcpController = (BluetoothAvrcpController) proxy;LogUtil.e(TAG, "onServiceConnected: mAvrcpController=" + mAvrcpController);//todo 第一次注册,这种情况需要更新播放状态,跟新媒体信息,播放进度break;}}@Overridepublic void onServiceDisconnected(int profile) {LogUtil.i(TAG, "onServiceDisconnected: profile=" + profile);switch (profile) {case BluetoothProfile.A2DP_SINK:mBluetoothA2dpSink = null;break;case BluetoothProfile.AVRCP_CONTROLLER:mAvrcpController = null;break;}}};

注销广播,注销监听

    public void unregisterBtReceiver(Context context) {if (mBtReceiver != null) {context.unregisterReceiver(mBtReceiver);mBtReceiver = null;}}
    public void unRegisterProfile() {mBluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP_SINK, mBluetoothA2dpSink);mBluetoothAdapter.closeProfileProxy(BluetoothProfile.AVRCP_CONTROLLER, mAvrcpController);}

广播中获取媒体信息和进度条信息

    /*** 更新歌曲基本信息ui** @param intent 广播回调的intent 在 BluetoothAvrcpController.ACTION_TRACK_EVENT 回调中*/private void updateMediaMetadata(Intent intent) {MediaMetadata mediaMetadata = intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_METADATA);String title = mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);String artist = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);String album = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM);String genre = mediaMetadata.getString(MediaMetadata.METADATA_KEY_GENRE);long totalTime = mediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION); //总时间更新,ms单位long currentTrack = mediaMetadata.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS);long totalTrack = mediaMetadata.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER);}
  /*** 主要用来实时更新当前播放进度, 广播回调的intent 在 BluetoothAvrcpController.ACTION_TRACK_EVENT 回调中** @param intent*/private void updatePlaybackState(Intent intent) {PlaybackState playbackState = intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_PLAYBACK);//更新播放状态 ,这里可以自己保存一个播放状态的标识if ((playbackState.getState() == PlaybackState.STATE_PLAYING)|| (playbackState.getState() == PlaybackState.STATE_FAST_FORWARDING)//快进|| (playbackState.getState() == PlaybackState.STATE_REWINDING)) {//快退updataPlayState(true);} else {updataPlayState(false);}long currentTime = playbackState.getPosition();//当前时间,ms为单位}

用a2dp的连接状态,来判断蓝牙音乐的打开,因为蓝牙有一个总开关,里面包含有用于蓝牙音乐的a2dp通道开关,一个是用于蓝牙电话的,这里主要是标识蓝牙是否可用的状态

    /*** 蓝牙a2dp连接状态。在BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED回调中** @param intent*/private void btA2dpContentStatus(Intent intent) {int a2dpSinkConnectionState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);switch (a2dpSinkConnectionState) {case BluetoothProfile.STATE_CONNECTED://这里重新获取了一下当前的设备信息,有些时候蓝牙断开了,设备信息是会被清空的mConnectedDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);//蓝牙音乐连接上,设置音源为true,里面会自动播放setAudioStreamMode(true);//这里有个特殊处理iphone,前提:iphone 会先走 adapter 后走 BluetoothA2dpSink,所以这再次获取一下设备信息initConnectDevice();break;case BluetoothProfile.STATE_DISCONNECTED:// 设置蓝牙为不可用状态,清空对应的一些标示位就行了break;}}

蓝牙开关连接状态

    /*** 蓝牙开关连接状态** @param intent*/private void btContentStatus(Intent intent) {int currentContentStatus = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, -1);switch (currentContentStatus) {case BluetoothAdapter.STATE_DISCONNECTED:LogUtil.e(TAG, "蓝牙已经断开连接");//只有蓝牙断开设置才置为nullmConnectedDevice = null;//todo 处理一些播放状态,设备名字等,清空操作//蓝牙断开,蓝牙也不应该发声了setAudioStreamMode(false);break;case BluetoothAdapter.STATE_CONNECTING:LogUtil.e(TAG, "蓝牙正在连接");break;case BluetoothAdapter.STATE_CONNECTED:LogUtil.e(TAG, "蓝牙已经连接");//连接的操作这里就不处理了,蓝牙因为的连接操作,放到 BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED 这个回调中,更准确break;case BluetoothAdapter.STATE_DISCONNECTING:LogUtil.e(TAG, "蓝牙正在断开连接");break;}}

检查蓝牙名字,是否更新

  /*** 检查蓝牙名字,是否更新,在BluetoothDevice.ACTION_NAME_CHANGED 回调中** @param intent*/private void checkBtName(Intent intent) {BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);if (bluetoothDevice.equals(mConnectedDevice)) {//地址相等则更新名字updataName();}}

以上就是用到的一些需要在广播回调中处理的一些事情及使用方法

//####################分割线 蓝牙的广播部分 end#####################
//####################分割线 下面是需要提供的一些接口 start#####################

蓝牙接口部分

获取蓝牙设备的名字

首先获取BluetoothDevice 蓝牙设备的管理类,通过遍历方式获取

     //蓝牙开关状态,搜索蓝牙,配对蓝牙等private BluetoothAdapter mBluetoothAdapter;private BluetoothDevice mConnectedDevice = null;//蓝牙连接的设备public void initConnectDevice() {mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//mBluetoothAdapter为null概率很低,这里不做判断,系统一启动就会赋值Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();for (BluetoothDevice device : bondedDevices) {if (device.isConnected()) {mConnectedDevice = device;LogUtil.e(TAG, "蓝牙连接上的设备:mConnectedDevice=" + mConnectedDevice);}}}

然后根据 mConnectedDevice 获取设备的名字

    /*** 获得远端(手机端)已连接的蓝牙设备的名称*/public String getBTDeviceName() {return mConnectedDevice.getName();}

设置焦点和释放焦点

//系统没有记录蓝牙音乐是否出声状态,需要自己记录,false不可以出声,这个方法是系统修改的,原生没有算车机端特殊处理的private boolean mIsAudioStreamModeOn = false; public void setAudioStreamMode(boolean on) {boolean ret = mBluetoothAdapter.setAudioStreamMode(on);if (ret) {mIsAudioStreamModeOn = on;} else {mIsAudioStreamModeOn = false;}}

播放与暂停的使用

    /*** 播放与暂停*/public void sendPlayPauseCmd(boolean isAvrcpPlayStatus) {if (isAvrcpPlayStatus) {mAvrcpController.sendPassThroughCmd(mConnectedDevice, BluetoothAvrcp.PASSTHROUGH_ID_PLAY, BluetoothAvrcp.PASSTHROUGH_STATE_PRESS);mAvrcpController.sendPassThroughCmd(mConnectedDevice, BluetoothAvrcp.PASSTHROUGH_ID_PLAY, BluetoothAvrcp.PASSTHROUGH_STATE_RELEASE);} else {mAvrcpController.sendPassThroughCmd(mConnectedDevice, BluetoothAvrcp.PASSTHROUGH_ID_PAUSE, BluetoothAvrcp.PASSTHROUGH_STATE_PRESS);mAvrcpController.sendPassThroughCmd(mConnectedDevice, BluetoothAvrcp.PASSTHROUGH_ID_PAUSE, BluetoothAvrcp.PASSTHROUGH_STATE_RELEASE);}}

上一曲

    /*** 上一曲*/public void sendPastCmd() {mAvrcpController.sendPassThroughCmd(mConnectedDevice, BluetoothAvrcp.PASSTHROUGH_ID_BACKWARD, BluetoothAvrcp.PASSTHROUGH_STATE_PRESS);mAvrcpController.sendPassThroughCmd(mConnectedDevice, BluetoothAvrcp.PASSTHROUGH_ID_BACKWARD, BluetoothAvrcp.PASSTHROUGH_STATE_RELEASE);}

下一曲

   /*** 下一曲*/public void sendNextCmd() {mAvrcpController.sendPassThroughCmd(mConnectedDevice, BluetoothAvrcp.PASSTHROUGH_ID_FORWARD, BluetoothAvrcp.PASSTHROUGH_STATE_PRESS);mAvrcpController.sendPassThroughCmd(mConnectedDevice, BluetoothAvrcp.PASSTHROUGH_ID_FORWARD, BluetoothAvrcp.PASSTHROUGH_STATE_RELEASE);}

获取当前媒体信息

    /*** 获取当前媒体信息** @return*/public MediaMetadata getCurrentMediaInfo() {return mAvrcpController.getMetadata(mConnectedDevice);}

获取当前进度

   /*** 获取当前进度** @return*/public long getCurrentProgress() {return mAvrcpController.getPlaybackState(mConnectedDevice) == null ? 0 : mAvrcpController.getPlaybackState(mConnectedDevice).getPosition();}

获取当前媒体总时长

    /*** 获取当前媒体总时长** @return*/public long getCurrentTotleTime() {return mAvrcpController.getMetadata(mConnectedDevice) == null ? 0: mAvrcpController.getMetadata(mConnectedDevice).getLong(MediaMetadata.METADATA_KEY_DURATION);}

//####################分割线 下面是需要提供的一些接口 end#####################

以上是我使用到的一些东西,可留言,更新你需要的

这里有一些东西是隐藏文件,需要编译打开

项目地址:https://github.com/MironGsony/CarBluetoothMusic
如何编译android系统的隐藏文件:https://editor.csdn.net/md/?articleId=110139734

android 车载蓝牙音乐介绍相关推荐

  1. 给Android车载蓝牙音乐添加盘控控制功能

    1. 方向盘事件转换 假设方向盘是通过lin总线转换的,最终来到安卓侧就是标准的keyevent: /** Key code constant: Play/Pause media key. */ pu ...

  2. android蓝牙音乐之AVRCP介绍和使用

    引言 最近做的车载蓝牙音乐开发,遇到很多问题,记录一下.也是到处东拼西凑的,勉强看看吧. AVRCP:Audio/Video Remote Control Profile,音视频远端控制协议,所以该协 ...

  3. Android蓝牙音乐获取歌曲信息

    由于我在蓝牙开发方面没有多少经验,如果只是获取一下蓝牙设备名称和连接状态那么前面的那篇文章就已经足够了,接下来的内容是转自一个在蓝牙音乐方面颇有经验的开发者的博客,他的这篇文章对我帮助很大. 今天,先 ...

  4. Android手机ROM定制初级教程以及Android系统文件夹结构介绍【转】

    Android手机ROM定制初级教程以及Android系统文件夹结构介绍 一个完整的OM根目录会有以下几个文件夹及文件: data META-IN system boot.img  系统目录说明 1. ...

  5. Android 自定义音乐播放器实现

    Android自定义音乐播放器 一:首先介绍用了哪些Android的知识点: 1 MediaPlayer工具来播放音乐 2 Handle.因为存在定时任务(歌词切换,动画,歌词进度条变换等)需要由Ha ...

  6. java计算机毕业设计基于安卓Android的音乐论坛APP(源码+系统+mysql数据库+Lw文档)

    项目介绍 本文先提出了开发基于Android的音乐论坛系统的背景意义,然后通过功能性和非功能性分析阐述本系统的需求,然后从功能设计和数据库设计两方面进行系统的设计建模.在技术实现部分采用了Java作为 ...

  7. Android简易音乐重构MVVM Java版 -搭建项目(八)

    Android简易音乐重构MVVM Java版 -搭建项目(八) 关于 新版本配置 网易云音乐api版本更新 重构代码 新建app类继承Application 项目结构 定义BaseActivity. ...

  8. 【android】音乐播放器之设计思路

    学习Android有一个多月,看完了<第一行代码>以及mars老师的第一期视频通过音乐播放器小项目加深对知识点的理解.从本文开始,将详细的介绍简单仿多米音乐播放器的实现,以及网络解析数据获 ...

  9. 【android】音乐播放器之数据存储总结

    学习Android有一个多月,看完了<第一行代码>以及mars老师的第一期视频通过音乐播放器小项目加深对知识点的理解.从本文开始,将详细的介绍简单仿多米音乐播放器的实现,以及网络解析数据获 ...

  10. 【android】音乐播放器之service服务设计

    学习Android有一个多月,看完了<第一行代码>以及mars老师的第一期视频通过音乐播放器小项目加深对知识点的理解.从本文开始,将详细的介绍简单仿多米音乐播放器的实现,以及网络解析数据获 ...

最新文章

  1. Windows 中 FS 段寄存器
  2. python类装饰器详解-Python 类装饰器解析
  3. windows 通过批处理 修改环境变量
  4. java 布局实例,HarmonyOS Java UI之StackLayout布局示例
  5. STC学习:“FM收音机”原理与测试说明
  6. java12 - 7 排序的案例
  7. 关于OSS如何获得直接访问文件的路径,哪位朋友指点一二??感激不尽
  8. 纽约大学石溪分校计算机科学,纽约州立大学石溪分校的主要基本信息介绍
  9. 硕士阶段学习情况汇总
  10. Python编程基础题(2-求一元二次方程的解Ⅱ)
  11. DM36x Rate Control Modes
  12. 公开在线讲座|Tamer Özsu教授:图处理-全景式视角和开放性问题
  13. liquibase基本使用
  14. vcruntime140.dll不可用或缺少
  15. 【Godot】SkillNode 技能节点
  16. Linux安装Discuz论坛(centos 7)
  17. Ubuntu18笔记本安装nvidia显卡驱动
  18. java ldap 根ou_【LDAP】LDAP 中 CN, OU, DC 的含义
  19. P2P网络编程-3-案例实践:PubSub
  20. java计算机毕业设计物流站环境监测系统源码+lw文档+系统+数据库

热门文章

  1. 记在创口贴上的超实用IDEA Eclipse快捷方式
  2. 谷粒学苑上传视频踩坑
  3. Docker容器实现跨宿主机通信
  4. php表格 单元格,实例演示PhpSpreadsheet的单元格设置教程
  5. C++ Primer 第四章学习 —— “表达式”
  6. spring boot网上眼镜商场毕业设计-附源码241659
  7. 复盘2020年全球医疗行业:新冠疫苗争分夺秒、跨国药企押注中国、药企整合并购不断 | 医药观察...
  8. 删除idb无法启动 mysql 数据库
  9. Mac Spotlight 聚焦搜索
  10. 如何计算页面的浏览率