Android蓝牙开发—BLE(低功耗)蓝牙详细开发流程

        Android蓝牙开发前,首先要区分是经典蓝牙开发还是BLE(低功耗)蓝牙开发,它们的开发是有区别的,如果还分不清经典蓝牙和BLE(低功耗)蓝牙的小伙伴,可以先看Android蓝牙开发—经典蓝牙和BLE(低功耗)蓝牙的区别

本文是针对BLE蓝牙开发的,如果是经典蓝牙开发,可以看Android蓝牙开发—经典蓝牙详细开发流程

注意:蓝牙4.0只有android4.3或4.3以上才支持

简单介绍

基本概念

1、Generic Access Profile(GAP)

用来控制设备连接和广播,GAP使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。

2、Generic Attribute Profile(GATT)

通过BLE连接,读写属性类数据的Profile通用规范,现在所有的BLE应用Profile都是基于GATT的。

3、Attribute Protocol (ATT)

GATT是基于ATTProtocol的,ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据,每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。

4、Characteristic

Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。

5、Descriptor

对Characteristic的描述,例如范围、计量单位等。

6、Service

Characteristic的集合。例如一个service叫做“Heart Rate Monitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart ratemeasurement”的Characteristic。

7、UUID

唯一标示符,每个Service,Characteristic,Descriptor,都是由一个UUID定义。

Android ble api

1、BluetoothManager

通过BluetoothManager来获取BluetoothAdapter。 

2、BluetoothAdapter

代表了移动设备的本地的蓝牙适配器, 通过该蓝牙适配器可以对蓝牙进行基本操作,一个Android系统只有一个BluetoothAdapter,通过BluetoothManager获取。 

3、BluetoothDevice

扫描后发现可连接的设备,获取已经连接的设备,通过它可以获取到BluetoothGatt。

4、BluetoothGatt

继承BluetoothProfile,通过BluetoothGatt可以连接设备(connect),发现服务(discoverServices),并把相应地属性返回到BluetoothGattCallback,可以看成蓝牙设备从连接到断开的生命周期。

5、BluetoothGattService

服务,Characteristic的集合。

6、BluetoothGattCharacteristic

相当于一个数据类型,可以看成一个特征或能力,它包括一个value和0~n个value的描述(BluetoothGattDescriptor)。

7、BluetoothGattDescriptor

描述符,对Characteristic的描述,包括范围、计量单位等。

8、BluetoothProfile

一个通用的规范,按照这个规范来收发数据。 

9、BluetoothGattCallback

已经连接上设备,对设备的某些操作后返回的结果。

简单总结:当我们扫描后发现多个设备BluetoothDevice,每个设备下会有很多服务BluetoothGattService,这些服务通过service_uuid(唯一标识符)来区分,每个服务下又会有很多特征BluetoothGattCharacteristic,这些特征通过uuid来区分的,它是手机与BLE终端设备交换数据的关键。而BluetoothGatt可以看成手机与BLE终端设备建立通信的一个管道,只有有了这个管道,才有了通信的前提。

开发流程

开发的流程和经典蓝牙是一样的,都需要以下这几个步骤。

  • 开启蓝牙
  • 扫描蓝牙
  • 绑定蓝牙
  • 连接蓝牙
  • 通信

开启蓝牙

1.获取BluetoothAdapter对象

这里有两种方法可以获取到BluetoothAdapter对象(区别在于版本不同而已,没有太大的区别)

第一种通过BluetoothManager对象获取

final BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();

第二种通过单例获取

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

2.判断是否有蓝牙功能模块

    if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            //有蓝牙功能模块
        }

3.判断设备是否支持蓝牙

/**
 * 设备是否支持蓝牙  true为支持
 * @return
 */
public boolean isSupportBlue(){
    return mBluetoothAdapter != null;
}

4.判断蓝牙是否开启

/**
 * 蓝牙是否打开   true为打开
 * @return
 */
public boolean isBlueEnable(){
    return isSupportBlue() && mBluetoothAdapter.isEnabled();
}

5.开启蓝牙

  • 异步自动开启蓝牙
/**
 * 自动打开蓝牙(异步:蓝牙不会立刻就处于开启状态)
 * 这个方法打开蓝牙不会弹出提示
 */
public void openBlueAsyn(){
    if (isSupportBlue()) {
        mBluetoothAdapter.enable();
    }
}
  • 同步提示开启蓝牙
/**
 * 自动打开蓝牙(同步)
 * 这个方法打开蓝牙会弹出提示
 * 需要在onActivityResult 方法中判断resultCode == RESULT_OK  true为成功
 */
public void openBlueSync(Activity activity, int requestCode){
    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    activity.startActivityForResult(intent, requestCode);
}

6.权限处理

  • 处理6.0以下版本的权限

    在AndroidManifest里面添加权限

<!-- 使用蓝牙的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 扫描蓝牙设备或者操作蓝牙设置 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  • 处理6.0及以上版本的权限

    (1)在AndroidManifest里面添加权限

<!-- 使用蓝牙的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 扫描蓝牙设备或者操作蓝牙设置 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!--模糊定位权限,仅作用于6.0+-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--精准定位权限,仅作用于6.0+-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    (2)动态检查权限

/**
 * 检查权限
 */
private void checkPermissions() {
    String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};
    List<String> permissionDeniedList = new ArrayList<>();
    for (String permission : permissions) {
        int permissionCheck = ContextCompat.checkSelfPermission(this, permission);
        if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
            onPermissionGranted(permission);
        } else {
            permissionDeniedList.add(permission);
        }
    }
    if (!permissionDeniedList.isEmpty()) {
        String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]);
        ActivityCompat.requestPermissions(this, deniedPermissions, REQUEST_CODE_PERMISSION_LOCATION);
    }
}

/**
 * 权限回调
 * @param requestCode
 * @param permissions
 * @param grantResults
 */
@Override
public final void onRequestPermissionsResult(int requestCode,
                                             @NonNull String[] permissions,
                                             @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case REQUEST_CODE_PERMISSION_LOCATION:
            if (grantResults.length > 0) {
                for (int i = 0; i < grantResults.length; i++) {
                    if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                        onPermissionGranted(permissions[i]);
                    }
                }
            }
            break;
    }
}

    (3)开启GPS

/**
 * 开启GPS
 * @param permission
 */
private void onPermissionGranted(String permission) {
    switch (permission) {
        case Manifest.permission.ACCESS_FINE_LOCATION:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !checkGPSIsOpen()) {
                new AlertDialog.Builder(this)
                        .setTitle("提示")
                        .setMessage("当前手机扫描蓝牙需要打开定位功能。")
                        .setNegativeButton("取消",
                                new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        finish();
                                    }
                                })
                        .setPositiveButton("前往设置",
                                new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                                        startActivityForResult(intent, REQUEST_CODE_OPEN_GPS);
                                    }
                                })

                        .setCancelable(false)
                        .show();
            } else {
                //GPS已经开启了
            }
            break;
    }
}

    (4)检查GPS是否开启

/**
 * 检查GPS是否打开
 * @return
 */
private boolean checkGPSIsOpen() {
    LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
    if (locationManager == null)
        return false;
    return locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER);
}

扫描蓝牙

1.扫描周围蓝牙设备(配对上的设备有可能扫描不出来)

调用BluetoothAdapter的startLeScan()方法来实现开始搜索。此方法时需要传入 BluetoothAdapter.LeScanCallback参数。搜索到的蓝牙设备都会通过这个回调返回。

    private BluetoothAdapter mBluetoothAdapter;
    private boolean isScanning;//是否正在搜索
    private Handler mHandler;
    //15秒搜索时间
    private static final long SCAN_PERIOD = 15000;

    private void scanLeDevice(final boolean enable) {
        if (enable) {//true
            //15秒后停止搜索
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    isScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);
            isScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索
        } else {//false
            isScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);//停止搜索
        }
    }

2.BluetoothAdapter.LeScanCallback回调

    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            //这里是个子线程,下面把它转换成主线程处理
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    //在这里可以把搜索到的设备保存起来
                    //device.getName();获取蓝牙设备名字
                    //device.getAddress();获取蓝牙设备mac地址
                    //这里的rssi即信号强度,即手机与设备之间的信号强度。
                }
            });
        }

    };

3.停止扫描设备

 扫描设备是一个耗能的操作,在我们连接蓝牙之前如果扫描没有结束,应该先停止扫描。

mBluetoothAdapter.stopLeScan(mLeScanCallback);//停止搜索

绑定蓝牙

绑定蓝牙是在我们调用连接方法时去绑定的,不用我们手动的去调方法绑定。

连接蓝牙

1.调用BluetoothDevice的connectGatt()方法,此函数带三个参数:Context、autoConnect(boolean)和 BluetoothGattCallback 对象。调用后返回BluetoothGatt对象

//这个方法需要三个参数:一个Context对象,自动连接(boolean值,表示只要BLE设备可用是否自动连接它),和BluetoothGattCallback调用。
BluetoothGatt mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback);

2.BluetoothGattCallback的回调

BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyUpdate(gatt, txPhy, rxPhy, status);
        }

        @Override
        public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyRead(gatt, txPhy, rxPhy, status);
        }

        //当连接状态发生改变
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
        }

        //发现新服务,即调用了mBluetoothGatt.discoverServices()后,返回的数据
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
        }

        //调用mBluetoothGatt.readCharacteristic(characteristic)读取数据回调,在这里面接收数据
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
        }

        //发送数据后的回调
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//descriptor读
            super.onDescriptorRead(gatt, descriptor, status);
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//descriptor写
            super.onDescriptorWrite(gatt, descriptor, status);
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);
        }

        //调用mBluetoothGatt.readRemoteRssi()时的回调,rssi即信号强度
        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {//读Rssi
            super.onReadRemoteRssi(gatt, rssi, status);
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
        }
    };

3.断开连接

mBluetoothGatt.disconnect(); //主动断开连接

通信

1.写入数据,通过mBluetoothGatt.writeCharacteristic(characteristic)

/**
 * 向蓝牙发送数据
 */
public void dataSend(){
    //byte[] send={(byte) 0xaa,0x01,0x01,(byte)0x81,(byte) 0xff};
    byte[] send = new byte[20];
    send = hexStringToBytes(et_send.getText().toString());
    byte[] sendData=new byte[send.length+2];
    sendData[0]=(byte) 0xaa;
    sendData[sendData.length-1]=(byte) 0xff;
    for(int i=1;i<sendData.length-1;i++){
        sendData[i]=send[i-1];
    }
    Log.e("dataSend", bytesToHexString(sendData));
    Log.e("dataSend", linkLossService +"");
    mCharacteristic.setValue(sendData);
    boolean status = mBluetoothGatt.writeCharacteristic(mCharacteristic);
    Log.e("dataSend", status+"");
}

注意:写入的数据长度是有限制的,如果要改变这个限制,可以通过MTU来改变。

2.读取数据,通过mBluetoothGatt.readCharacteristic(characteristic),在BluetoothGattCallback 回调方法onCharacteristicRead中获取到数据。

//读取数据时调用这个方法,数据会在回调接口中(BluetoothGattCallback )获取到
mBluetoothGatt.readCharacteristic(characteristic)


BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {     

        //调用mBluetoothGatt.readCharacteristic(characteristic)读取数据回调,在这里面接收数据
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            //这里面就是数据
            characteristic.getValue();
        }
}

总结

首先,我们要判断手机是否支持BLE,并且获得各种权限,才能让我们之后的程序能正常运行。 
然后,我们去搜索BLE设备,得到它的MAC地址。 
其次,我们通过这个MAC地址去连接,连接成功后,去遍历得到Characteristic的uuid。 
在我们需要发送数据的时候,通过这个uuid找到Characteristic,去设置其值,最后通过writeCharacteristic(characteristic)方法发送数据。 
如果我们想知道手机与BLE设备的距离,则可以通过readRemoteRssi()去得到rssi值,通过这个信号强度,就可以换算得到距离。 
只要我们连接上,我们就可以用BluetoothGatt的各种方法进行数据的读取等操作。

以上就是BLE(低功耗)蓝牙的开发流程和部分代码,后期会提供demo下载。若有不当之处,请留言讨论,一起学习进步。

猜你喜欢

转载自blog.csdn.net/zqf_888/article/details/81196766