欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 蓝牙4.0 Ble开发(一)

程序员文章站 2022-03-16 17:11:16
...

一,Ble 4.0简介

低功耗蓝牙,低成本、短距离、可互操作的鲁棒性无线技术,从蓝牙4.0开始支持,Android端是从Android4.3开始支持ble,但是是从android5.0开始支持手机作为外设端开发,在此(5.0)之前只能作为*设备开发。

二,API

BluetoothAdapter  蓝牙适配器,通过它来获取蓝牙地址、蓝牙名字、绑定设备、扫描模式、蓝牙状态等参数(后面代码有详细说明)
BluetoothLeScanner 蓝牙扫描类
BluetoothDevice 蓝牙设备类
BluetoothGatt 蓝牙通信类,通过这个类来建立通信通道
BluetoothGattService 蓝牙通信通道服务类
BluetoothGattCallback:*设备回调类。
BluetoothGattServer:外设提供数据类;
BluetoothGattServerCallback:外设回调类
BluetoothGattCharacteracteristic 蓝牙通信通道特征值类
BluetoothGattDescriptor 蓝牙通信通道特征值属性类

一个ble终端可以包含多个Service,一个Service可以包含多个Characteristic,一个Characteristic包含一个value和多个Descriptor,一个Descriptor包含一个value。其中Characteristic比较重要,用的比较多。Descriptor主要对Characteristic进行范围、单位的描述。

三,ble的分类

    *设备(central):进行扫描,启动连接,在单一或多链路层作为主机。
    外围设备(periphery):广播发送者,可连接的设备,在单一链路层作为从机。
    广播者(Braodcaster):广播发送者,是不可连接的设备。
    观察者(Observer):扫描广播,不能够启动连接。
广播者和观察者不能建立连接。应用:温度传感器和温度显示器。
一个*可以同时连接多个周边,但是一个周边只能连接一个*。

四,作为*设备示例

    1.初始化Ble
private void initBle() {
        //获取蓝牙管理类对象
        BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
        //获取蓝牙适配器
        adapter = manager.getAdapter();
        //判断是否支持蓝牙(此处并非是Ble)
        if (adapter == null) {
            Log.d(tag, "该设备没有蓝牙");
            return;
        } else {
            Log.d(tag, adapter.getAddress() //蓝牙地址
            + "\n" + adapter.getName()   //蓝牙名字
            + "\n" + adapter.getBondedDevices()//已匹配的设备
            + + "\n" + adapter.getScanMode() //扫描模式
            + "\n" + adapter.getState());//状态
            //判断蓝牙打开状态如果没打开直接打开
            if (!adapter.isEnabled()) {
                adapter.enable();
         }
//当然此处也可采用,通过用户点击来打开蓝牙
// Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);            //enableBtIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//context.startActivity(enableBtIntent);
}
//判定是否支持Ble
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, "该设备上不支持BLE", Toast.LENGTH_SHORT).show();
} else {
     Toast.makeText(this, "该设备上支持BLE", Toast.LENGTH_SHORT).show();
 }
     mac.setText("本机的蓝牙mac\n" + adapter.getAddress());
 }
2.扫描

    // lescan开始扫描ble设备
private void startLeScan(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Log.e(tag,"start bleScan");
        //获取扫描对象       mBluetoothLeScanner=adapter.getBluetoothLeScanner();
        //扫描结果的集合
 List<ScanFilter>bleScnFilter=new ArrayList<>();
 bleScnFilter.add(new ScanFilter.Builder().setDeviceAddress(address).build());
 //扫描设置
ScanSettings bleScanSetting= new ScanSettings.Builder().build();
//开始扫描
mBluetoothLeScanner.startScan(null, bleScanSetting, mBleScanCallback);
}else{
        Log.e(tag,"build not support LeScan");
}
}
3.扫描回调
    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
    }
//扫描结果回调方法
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
        Log.e(tag, "bleScan success");
        //扫描的设备名字
            Log.e(tag, "device name:" + result.getDevice().getName());
            //扫描的设备蓝牙地址
            Log.e(tag, "device address:" + result.getDevice().getAddress());
            //扫描的设备通道的UUID
            Log.e(tag, "device Services UUID:" + result.getDevice().getUuids());
            //扫描的设备广播标志
            Log.e(tag, "record advertise flags:" + Integer.toHexString(result.getScanRecord().getAdvertiseFlags()));
            //蓝牙传输功率
            Log.e(tag, "record Tx power level:" + result.getScanRecord().getTxPowerLevel());
            //设备名称
            Log.e(tag, "record device name:" + result.getScanRecord().getDeviceName());
            //服务的UUID
            Log.e(tag, "record services UUID:" + result.getScanRecord().getServiceUuids());
           //广播的数据
            Log.e(tag, "record services data:" + result.getScanRecord().getServiceData());
            //广播的数据
            Log.e(tag, "record services data2:" + result.getScanRecord().getServiceData());
            //广播的特征值的UUID
readuuid= result.getDevice().getAddress().toString();
    }
    //扫描失败回调方法
    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
        Log.e(tag, "Blescan fail " + errorCode);
    }
};
4.停止扫描
 private void stopScnning(){
 //应该先判断是否实例化扫描对象,否则会出现空指针异常
     if (mBluetoothLeScanner!=null){
      Log.e(tag,"stop scnning");
      mBluetoothLeScanner.stopScan(mBleScanCallback);
     }
5.连接
//这里的连接是根据蓝牙地址直接来进行连接的
private void connectBluetooth() {
        Log.e(tag,"conectBluetooth");
        String contents=content.getText().toString();
            devices=adapter.getRemoteDevice(contents);
            if (devices==null){
                Log.e(tag,"devices==null");
            }else{
        //得到gatt通信的对象  mBluetoothGatt=devices.connectGatt(this,false,mGattCallback); 
         //连接
         mBluetoothGatt.connect();
            }
        }
    }
6.连接回调
private final BluetoothGattCallback mGattCallback=new BluetoothGattCallback() {
        //连接状态改变
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            if (mBluetoothGatt==null){
                Log.e(tag,"mBluetooth is null");
                return;
            }
            if (newState== BluetoothProfile.STATE_CONNECTED){
                Log.e(tag,"已连接");
                //连接上去执行此方法,获取通信服务
                gatt.discoverServices();
                SimpleDateFormat formatter    =   new SimpleDateFormat("yyyy:MM:dd HH:mm:ss:SSS");
                date2    =   new Date(System.currentTimeMillis());//获取当前时间
                str2    =    formatter.format(date2);
                long date3=date2.getTime()-date1.getTime();
                msg.setText("开始时间:"+str1+"\n"+"连接完成:"+str2+"\n连接时间差:"+date3+"ms");
                Toast.makeText(MainActivity.this,"连接成功",Toast.LENGTH_SHORT).show();
            }else  if (newState== BluetoothProfile.STATE_CONNECTING){
                Log.e(tag,"正在连接");
            }else  if (newState== BluetoothProfile.STATE_DISCONNECTED){
                Log.e(tag,"断开连接"+status+newState);
            }else  if (newState== BluetoothProfile.STATE_DISCONNECTING){
                Log.e(tag,"正在断开连接");
            }
        }
        //当通道写入的时候回调
         @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            Log.e(tag,"write state"+status);
        }
//当D发现可用通道服务的时候回调
        @Override//onServicesDiscovered
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.e(tag,"有可用服务"+gatt.getServices().toString());
            if (status==BluetoothGatt.GATT_SUCCESS){
                List<BluetoothGattService> service=gatt.getServices();
//                getUUID(service);
                Log.e(tag,"有可用服务");
                Toast.makeText(MainActivity.this,"有可用服务",Toast.LENGTH_SHORT).show();
            }else if (status==BluetoothGatt.GATT_FAILURE){
                Log.e(tag,"没有发现可用服务");
                Toast.makeText(MainActivity.this,"没有发现可用服务",Toast.LENGTH_SHORT).show();
            }else{
                Log.e(tag,"没有发现可用服务"+status);
                Toast.makeText(MainActivity.this,"没有发现可用服务2",Toast.LENGTH_SHORT).show();
            }
        }
//当通道数据发送改变的时候回调
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            Log.e(tag,characteristic+"");
            Toast.makeText(MainActivity.this,"数据发生变化",Toast.LENGTH_LONG).show();
            if (characteristic.getValue()!=null){
                Log.e(tag,"数据发生变化");
            }else{
                Log.e(tag,"数据未发生变化");

            }
        }
        //当通道被读的时候回调
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (status==BluetoothGatt.GATT_SUCCESS){
                Log.e(tag,characteristic.getValue()+"我是分割线"+status);
                if (characteristic.getValue().equals(str));
            }

        }
//当Descriptor写入的时候回调
        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            String uuid=descriptor.getValue().toString();
            Log.e(tag,uuid+"");

        }
    };
7.断开连接
 private void cutConnectBluetooth(){
        if (devices==null){
            Log.e(tag," devices or mBluetoothGatt is null");
        }else{
            if (mBluetoothGatt==null){
                Log.e(tag,"mBluetooth is isconnect");
                return;
            }else{
                Log.e(tag,"do disconnect mBluetooth");
                mBluetoothGatt.disconnect();
                mBluetoothGatt=null;
            }
        }
    }
注意的是必须在调用的页面销毁的时候关闭连接关闭通道,否则会崩溃。
8.最后的最后
    权限问题不要忘了!
    在你的app manifest文件中声明蓝牙权限。
    <uses-permission                 android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
如果想声明你的app只为具有BLE的设备提供,在manifest文件中包括:
<uses-featureandroid:name="android.hardware.bluetooth_le" android:required="true"/>

结语:第一次想把自己爬过的坑,能分享给大家。而且蓝牙这块资料很少,一路过来很多坑,所以在项目截止今天比较有时间的时候,静下心,写了下来,分享给大家,希望能一起进步!Demo在我把外设端写完的时候一起上传!有写的不对的理解不对的地方,万望指正!