Android 蓝牙编程

Android 蓝牙开发分两种:

  • 经典蓝牙开发

    蓝牙3.0及以下版本的蓝牙,都称为“经典蓝牙”。特点是功耗高、传输数据量大、传输距离只有10米。主要场景有蓝牙耳机、蓝牙音箱等。是近距离音频传输的不二选择

  • 低功耗蓝(BLE)牙开发

    蓝牙4.0及以上版本,低功耗,传输数据量小,距离50米左右。主要场景有共享单车锁、蓝牙防丢器等,是目前手机和智能硬件通信性价比最高的手段

一般较新的手机都是双模蓝牙,即同时支持经典蓝牙和低功耗蓝牙。

关蓝牙开发的官方文档教程,请查看 这里

以经典蓝牙开发为例

  1. 开启蓝牙
  2. 扫描蓝牙
  3. 配对蓝牙
  4. 连接蓝牙
  5. 通信

权限配置:

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

对于一些BLE的低功耗蓝牙设备,需要用到上述的一个位置相关的权限,否则可能存在扫描不到这种设备的情况。但是需要注意,该权限在6.0以上设备,属于动态权限,请处理动态权限申请相关的事宜。

操作代码:

public class BluetoothHelper {

    public BluetoothHelper(){
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    private BluetoothAdapter mBluetoothAdapter;

    // 设备是否支持蓝牙
    public boolean isSupport() {
        return mBluetoothAdapter != null;
    }

    // 蓝牙是否已经开启
    public boolean isOpen() {
        return isSupport() && mBluetoothAdapter.isEnabled();
    }

    // 向用户请求开启蓝牙。同步执行
    public void requestBluetooth(int requestCode, Activity context) {
        if (isSupport()){
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            context.startActivityForResult(enableIntent, requestCode);
        }
    }

    // 静默开启蓝牙(不会弹出提示)。异步执行
    public void openBluetooth(){
        if(isSupport()){
            mBluetoothAdapter.enable();
        }
    }

    // 关闭蓝牙适配器
    public void closeBluetooth(){
        if (isSupport()){
            mBluetoothAdapter.disable();
        }
    }

    // 查询已配对设备
    public List<BluetoothDevice> checkDevices() {
        List<BluetoothDevice> devices= new ArrayList<>();
        if(isSupport()){
            Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
            if (pairedDevices != null && pairedDevices.size() > 0) {
                for (BluetoothDevice device : pairedDevices) {
                    devices.add(device);
                }
            }
        }
        return devices;
    }

    // 扫描新设备
    public void scanNewDevice() {
        // 该方法立即返回一个布尔值,表示扫描是否已成功启动。
        // 内部会启动了一个子线程执行扫描,扫描通常需要大约12秒
        if(isOpen() && !mBluetoothAdapter.isDiscovering()){
            if (mBluetoothAdapter.startDiscovery()) {
                Log.d("","-- 已成功启动寻找新设备 --");
            } else {
                Log.d("","-- 启动寻找新设备失败 --");
            }
        }
    }

    // 取消扫描。返回 true 表示成功取消
    public boolean cancelScan(){
        return isSupport()
                && mBluetoothAdapter.isDiscovering()
                &&  mBluetoothAdapter.cancelDiscovery();
    }

    public void register(Context ctx){
        IntentFilter intentFilter = new IntentFilter();
        // 开始扫描
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        // 扫描结束(重新扫描时,会先终止先前的扫描)
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        // 扫描到设备(每扫描一个发送一条广播)
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
        // 本地蓝牙适配器的状态更改(蓝牙打开或关闭)
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        // 蓝牙适配器的连接状态改变(另一个配对设备自己把连接断开)
        intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
//        intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
        ctx.registerReceiver(mBluetoothReceiver,intentFilter);
    }

    public void unregister(Context ctx){
        ctx.unregisterReceiver(mBluetoothReceiver);
    }

    private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                Log.d("BluetoothHelper","设备名:"+device.getName()+" *=* " +"MAC地址:"+device.getAddress());
            }else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)){
                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
                switch (state) {
                    case BluetoothAdapter.STATE_TURNING_ON:
                        Log.d("BluetoothHelper","蓝牙正在打开.");
                        break;
                    case BluetoothAdapter.STATE_ON:
                        Log.d("BluetoothHelper","蓝牙已打开.");
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        Log.d("BluetoothHelper","蓝牙正在关闭.");
                        break;
                    case BluetoothAdapter.STATE_OFF:
                        Log.d("BluetoothHelper","蓝牙已关闭.");
                        break;
                }
            }else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)){
                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, -1);
                switch (state) {
                    case BluetoothAdapter.STATE_CONNECTING:
                    case BluetoothAdapter.STATE_CONNECTED:
                    case BluetoothAdapter.STATE_DISCONNECTING:
                    case BluetoothAdapter.STATE_DISCONNECTED:
                        break;
                }
            }
        }
    };
}

需要注意,以向用户请求的方式开启蓝牙,应当在Activity中处理返回结果:

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_ENABLE_BLUETOOTH) {
            if (resultCode == RESULT_OK) {
                // 蓝牙打开成功
            } else {
                // 蓝牙打开失败
            }
        }
    }

判断设备是否处于连接中

注意,BluetoothAdapter.getBondedDevices()获取的是已配对设备列表,要想确定设备是否处于连接中,通过查询源码,可知BluetoothDevice类中有一个isConnected方法,但该方法是隐藏的,非对外公开的API,因此只能通过反射调用

for (BluetoothDevice device : pairedDevices) {
    Method isConnectedMethod = null;
    try {
        isConnectedMethod = BluetoothDevice.class.getDeclaredMethod("isConnected");
        boolean isConnected = (boolean) isConnectedMethod.invoke(device);
        Log.d("BluetoothHelper","Devices:"+device.getName()+" = "+isConnected);
    } catch (Exception e) {
        Log.e("BluetoothHelper",e.getMessage());
        e.printStackTrace();
    }
}

如果只是判断蓝牙耳机是否连接,可以增加以下方法:

public boolean isBluetoothHeadsetConnected() {
    int a2dp = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.A2DP);
    int headset = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET);
    return isOpen() && (
            headset == BluetoothHeadset.STATE_CONNECTED 
            || a2dp == BluetoothA2dp.STATE_CONNECTED);
}

拓展

如果需要通过代码来控制配对(新版本已不允许使用代码操作进行配对)

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void pin(BluetoothDevice device){
    //配对之前关闭扫描
    if (mBluetoothAdapter.isDiscovering()){
        mBluetoothAdapter.cancelDiscovery();
    }
    //判断设备是否配对,已配对则不需配对了
    if (device.getBondState() == BluetoothDevice.BOND_NONE) {
        device.createBond();
    }
}

createBond是一个异步调用的方法,它不会返回配到的结果,如需获取蓝牙是否配对成功的结果,需要监听广播

// 来自其他设备的配对请求广播
intentFilter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
// 当前配对状态广播
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);

处理广播:

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();
    if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)){
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        //1.确认配对
        device.setPairingConfirmation(true);
        //2.终止有序广播
        abortBroadcast();
        //3.调用setPin方法进行配对
        device.setPin("0000".getBytes());
    }else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
        int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
        switch (state) {
            case BluetoothDevice.BOND_NONE:
                // 取消配对
                break;
            case BluetoothDevice.BOND_BONDING:
                // 配对中
                break;
            case BluetoothDevice.BOND_BONDED:
                // 配对成功
                break;
        }
    }
}

注意:执行上述操作,需要申请<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />权限,但是新版本上,已经不允许第三方APP申请该权限


公众号“编程之路从0到1”

20190301102949549

Copyright © Arcticfox 2021 all right reserved,powered by Gitbook文档修订于: 2022-05-01 12:20:20

results matching ""

    No results matching ""