Android蓝牙音乐

一 蓝牙权限

在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" />

二 蓝牙的连接

短距离无线通信技术

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() {
        @Override
        public 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的监听,主要是处理一些,应用还未起来,蓝牙已经连接上了,有些广播不走,需要通过这里来处理

注销广播,注销监听

    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, "蓝牙已经断开连接");
                //只有蓝牙断开设置才置为null
                mConnectedDevice = 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();
        }
    }

四 蓝牙接口部分

获取蓝牙设备的名字

首先获取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
                :
    }

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Android蓝牙音乐播放流程主要分为以下几个步骤: 1. 确认设备配对和连接:首先,用户需要通过系统设置将手机与蓝牙音频设备进行配对并建立连接。在设备配对完成后,Android系统会为配对的设备分配一个唯一的蓝牙地址。 2. 打开蓝牙音乐应用程序:用户在手机上打开一个蓝牙音乐播放应用程序,例如Spotify或Google音乐。这个应用程序会向系统发出请求来获取蓝牙音频设备的当前状态。 3. 启用蓝牙音频传输:应用程序收到设备状态的响应后,会请求系统启用蓝牙音频传输。这样,手机就可以通过蓝牙与配对的设备之间进行音频数据传输。 4. 播放音乐:一旦蓝牙音频传输启用,应用程序将开始播放音乐。它会将音乐数据流传输到蓝牙音频设备,并且设备会输出音频信号,使用户能够听到音乐。 5. 音乐控制:在音乐播放过程中,应用程序可以接收来自蓝牙音频设备的指令,例如播放、暂停、上一曲、下一曲等。同时,应用程序也可以向蓝牙音频设备发送控制指令,例如调整音量。 6. 断开连接和关闭应用程序:当用户不再需要使用蓝牙音频设备时,可以通过系统设置断开连接。此外,用户还可以关闭蓝牙音乐播放应用程序,以停止音乐播放并释放系统资源。 总而言之,Android蓝牙音乐播放流程涉及设备配对和连接、应用程序的启用和音频传输、音乐播放和控制,以及断开连接和关闭应用程序等步骤。这个流程使得用户能够通过蓝牙与音频设备进行无线音乐播放。 ### 回答2: Android 蓝牙音乐播放的流程如下: 首先,需要确保设备上的蓝牙功能已经开启,并且与外部音频设备(如蓝牙耳机或扬声器)配对成功。 在Android应用中,首先需要通过BluetoothAdapter类来获取设备的蓝牙适配器实例。然后,通过调用适配器的方法来检查蓝牙是否打开,以及是否已经与外部设备配对。 接下来,需要通过BluetoothProfile类来建立与外部音频设备的连接,并且获得BluetoothA2dp或者BluetoothHeadset对象。这些对象提供了蓝牙音频传输的功能。 一旦获得了BluetoothA2dp或者BluetoothHeadset对象,就可以使用它们的方法来获取已连接的蓝牙设备列表。从中选取想要连接的设备,并调用其连接方法。 连接成功后,需要通过获取BluetoothA2dp或BluetoothHeadset的BluetoothDevice对象,并使用其方法来获取支持的音频设备列表。选择一个合适的音频设备,然后使用它的方法来启动音频播放。 在音频播放过程中,可以使用MediaPlayer类或者其他音频处理库来播放音乐。这些库通常提供了控制音频播放、暂停、停止等功能。 最后,当不再需要播放音乐时,可以调用BluetoothA2dp或BluetoothHeadset的相应方法来停止音频播放,并且释放掉之前的连接。 总结起来,蓝牙音乐播放的流程包括:开启蓝牙功能,建立与外部音频设备的连接,获取支持的音频设备列表,选择音频设备并启动音频播放,控制音频播放过程,最后释放连接。 ### 回答3: Android蓝牙音乐播放的流程如下: 首先,需要确保设备的蓝牙功能已经打开并且已经连接到音频输出设备(如蓝牙耳机或蓝牙音箱)。 其次,需要获取蓝牙适配器并检查是否支持蓝牙功能。然后,搜索周围可配对的蓝牙设备,并获取相应的设备列表。 接下来,需要选择要连接的音频设备。可以根据设备名称或其他可供识别的信息来选择。 连接到音频设备后,需要建立蓝牙音频通信通道。这通常涉及到与音频设备进行配对,然后通过BluetoothSocket来建立连接。 完成蓝牙音频通信通道的建立后,就可以开始传输音频数据了。通常会创建一个用于传输音频数据的线程,通过BluetoothSocket读取音频数据并将其传输到音频设备。 同时,为了实现音乐播放功能,还需要使用Android的多媒体框架(如MediaPlayer)来解码和播放音频数据。可以将从蓝牙设备读取的音频数据转发给MediaPlayer进行解码和播放。 在音乐播放期间,还需要处理状态变化和用户操作(如播放、暂停、停止等)。可以通过监听蓝牙设备的状态变化和用户的操作来相应地调用对应的播放控制命令。 最后,需要在应用程序结束或用户停止播放时,释放蓝牙连接和关闭相关的流。 以上是简单的介绍了Android蓝牙音乐播放的流程,具体的实现方式会根据开发者的需求和具体情况而有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值