前言部分

最近因为需要开始蓝牙相关开发,所以在网上搜索了很多内容,并且结合自己的开发过程做了一个总结,先储备上,也许可能帮到正在做蓝牙开发的同学。

蓝牙很早就是android设备上基本通讯功能了,只是以前的没有那么多蓝牙设备,现在蓝牙设备种类繁多,所以经常会有人遇到蓝牙相关的开发。

官方说明文档

官方例子

内容部分

以下部分内容都是比较常规的,很多博客都已经写过了,但是为了记录一个完整的开发流程,这里还是按部就班的都写出来。

1.首先是第一步,打开手机的蓝牙开关。

//获取蓝牙适配器
final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

//通过适配器判断蓝牙是否可用
if (!mBluetoothAdapter.isEnabled()) {
                    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
                }

2.打开蓝牙后就可以获取已经配对的设备列表。

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
                    if (pairedDevices.size() > 0) {
                        mList.clear();
                        mArrayAdapter.notifyDataSetChanged();
                    }
//这里是做了一个数据交换,将Set里面的数据copy到List中,主要是为了根据角标来取出数据。
                    Iterator<BluetoothDevice> iterator = pairedDevices.iterator();
                    while (iterator.hasNext()) {
                        mList.add(iterator.next());
                    }
//另外一种方式。
//                    mList.addAll(pairedDevices);
                    listView.setVisibility(View.VISIBLE);
                    mArrayAdapter.notifyDataSetChanged();
                }

3.下面就可以扫描附近的设备了,这里要注意一下权限的问题,因为在6.0以后的版本需要添加一个位置权限,如下:

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

动态权限申请,就不介绍了,网上例子很多,也有很多库。扫描比较简单只需要调用一个方法就可以了,但是应该要控制一下扫描停止的时间,因为扫描蓝牙设备是很耗费资源的。

if (!mBluetoothAdapter.isDiscovering()) {
            if (mBluetoothAdapter.startDiscovery()) {
                Toast.makeText(getApplicationContext(), "启动扫描中", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "启动扫描中失败", Toast.LENGTH_SHORT).show();

            }
        } else {
            Toast.makeText(getApplicationContext(), "扫描中,请勿重复点击", Toast.LENGTH_SHORT).show();
        }

开启扫描后,当有新的蓝牙设备被发现,系统会发送广播,我们只需要监听相应的官博即可。下面是需要监听的action,官方例子没有这么多,你可以按需要添加减少。

// Register the BroadcastReceiver
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothDevice.ACTION_FOUND);//搜索发现设备
        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//状态改变
        filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);//行动扫描模式改变了
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//动作状态发生了变化
        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
        filter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);

下面是扫描到新设备后接受的action事件,我们需要在这里更新我们界面上的蓝牙设备类表。

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        public void onReceive(final Context context, Intent intent) {
            String action = intent.getAction();
            // When discovery finds a device
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // Get the BluetoothDevice object from the Intent
                device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//                 Add the name and address to an array adapter to show in a ListView
                mList.clear();
                pairedDevices.add(device);
                Iterator<BluetoothDevice> iterator = pairedDevices.iterator();
                while (iterator.hasNext()) {
                    mList.add(iterator.next());
                }
                connectBluetoothAdapter.notifyDataSetChanged();
                //这里我是扫描到需要的设备就停止扫描动作了。
                if (!TextUtils.isEmpty(device.getName())) {
                    if (device.getName().contains("BestSolo")) {
                        stopDiscovery();
     
                    }
                }

            } 
    };

4.关键的一步,到拿到扫描设备后,我们就可以连接到设备上了,但是因为蓝牙2.0并没有提供连接的方法,这里连接需要反射调用系统方法。

//connect是隐藏的方法,只能通过反射机制来调用
    private void connect(BluetoothA2dp mBluetoothA2dp) {
        if (mBluetoothA2dp == null) {
            LogUtil.d("mBluetoothA2dp==null" + device.getName());
            return;
        }
        if (device == null) {
            return;
        }


        try {
            LogUtil.d("connect" + device.getName());
            Method connect = mBluetoothA2dp.getClass().getDeclaredMethod("connect", BluetoothDevice.class);
            connect.setAccessible(true);
            connect.invoke(mBluetoothA2dp, device);
        } catch (Exception e) {
            LogUtil.e("connect exception:" + e);
            e.printStackTrace();
        }
    }

因为是通过反射调用,所以调用连接后的效果和系统蓝牙连接效果一致,举个例子:

我用demo连接了我的蓝牙耳机,实际的效果是手机连接了耳机,就是所有的音频都通过蓝牙输出至耳机。 所以这个还是和4.0的蓝牙很不一致的。

上面连接方法需要一个蓝牙的代理BluetoothA2dp,下面是对该类的解释。

/**
 * This class provides the public APIs to control the Bluetooth A2DP
 * profile.
 *
 * <p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
 * the BluetoothA2dp proxy object.
 *
 * <p> Android only supports one connected Bluetooth A2dp device at a time.
 * Each method is protected with its appropriate permission.
 */

获取BluetoothA2dp的方法是通过蓝牙适配器来获取的。

private BluetoothA2dp mBluetoothA2dp;

    private void getBluetoothA2DP() {
        mBluetoothAdapter.getProfileProxy(getBaseContext(), mListener, BluetoothProfile.A2DP);
    }

    private BluetoothProfile.ServiceListener mListener = new BluetoothProfile.ServiceListener() {
        @Override
        public void onServiceDisconnected(int profile) {
            if (profile == BluetoothProfile.A2DP) {
                mBluetoothA2dp = null;
                LogUtil.e("onServiceDisconnected :" + profile);

            }
        }

        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            if (profile == BluetoothProfile.A2DP) {
                mBluetoothA2dp = (BluetoothA2dp) proxy; //转换
                LogUtil.e("onServiceConnected :" + mBluetoothA2dp.toString());
                connect(mBluetoothA2dp);
            }
        }
    };
  1. 上面的方法都走完,基本上蓝牙已经连接成功了,然后你就做一些操作数据的动作了,如我连接的蓝牙耳机,现在就可以在demo 中播放音频文件了,是可以正常听到的。

结尾

基本上这个流程上完整的,不过现在蓝牙开发基本上还是用BLE低功耗蓝牙的多,新的蓝牙版本提供了新的扫描方法,也提供了连接设备方法,可以通过UUID来连接指定的蓝牙设备。

下篇继续写新的蓝牙版本开发文章。