分享

蓝牙BLE通讯

 beginnow1 2022-09-23 发布于广东

程序界面

在搞安卓蓝牙通讯前,我把安卓的知识都忘光了,这次也再次快速入门了安卓APP编程,可以参考我上一篇文稿,也把蓝牙通讯的界面也给简单的做出来了,也就不再重复。

http://www.360doc.com/content/22/0922/19/40492717_1048959289.shtml

批量自动申请权限

首先还是解决APP权限的问题,因为蓝牙能有定位功能,所以Android 6.0以上版本还需要动态申请位置权限!如果没有权限会会出现闪退的情况。

首先AndroidManifest.xml文件,声明好权限

    <!-- 应用使用蓝牙的权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <!-- 启动设备发现或操作蓝牙设置的权限-->
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <!-- 位置权限 -->
    <!-- Android 10以上系统,需要ACCESS_FINE_LOCATION -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- Android 9以及以下系统,需要ACCESS_FINE_LOCATION -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <!-- 设备读写权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

第二步封装一个申请权限的工具类Permission.java,这个可以批量申请权限

package com.example.bt_communication;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.util.ArrayList;
import java.util.List;

public class Permission {
    //需要申请权限的数组
    private String[] permissions = {
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.BLUETOOTH_ADMIN,
            Manifest.permission.BLUETOOTH,
            //API 31以上才需要额外申请以下权限
//            Manifest.permission.BLUETOOTH_CONNECT,
//            Manifest.permission.BLUETOOTH_SCAN,
    };
    //保存真正需要去申请的权限
    private List<String> permissionList = new ArrayList<>();

    public static int RequestCode = 100;

    public void checkPermissions(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            for (int i = 0; i < permissions.length; i++) {
                //确认不是普通权限,则添加到列表
                if (ContextCompat.checkSelfPermission(activity, permissions[i]) != PackageManager.PERMISSION_GRANTED) {
                    permissionList.add(permissions[i]);
                }
            }
            //有需要去动态申请的权限
            if (permissionList.size() > 0) {
                requestPermission(activity);
            }
        }
    }

    //去申请的权限
    public void requestPermission(Activity activity) {
        ActivityCompat.requestPermissions(activity, permissionList.toArray(new String[permissionList.size()]), RequestCode);
    }

}

最后在mainActivity里加上执行代码

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Permission permission = new Permission();
        permission.checkPermissions(this);
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == Permission.RequestCode) {
            for (int i = 0; i < grantResults.length; i++) {
                if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                    Log.e("p","拒绝的权限名称:" + permissions[i]);
                    Log.e("p","拒绝的权限结果:" + grantResults[i]);
                    Log.e("p","有权限未授权,可以弹框出来,让客户去手机设置界面授权。。。");
                }else {
                    Log.e("p","授权的权限名称:" + permissions[i]);
                    Log.e("p","授权的权限结果:" + grantResults[i]);
                }
            }
        }
    }
}

蓝牙通讯基本流程

蓝牙扫描:bluetoothLeScanner.startScan(leScanCallback);

扫描回调函数:leScanCallback回调里的onScanResult处理扫描结果,添加到textView显示,如果是想要的Nordic_UART蓝牙名称,自动连接服务

开启蓝牙服务:BluetoothLeService.class是封装好的蓝牙service服务,绑定开启蓝牙的后台服务,serviceConnection是服务连接的回调函数。

Intent gattServiceIntent = new Intent(MainActivity.this,BluetoothLeService.class);
bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);  //绑定开启蓝牙的后台服务

蓝牙连接:在ServiceConnection的服务回调里的onServiceConnected连接蓝牙,停止扫描

bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback); //连接蓝牙获取GATT
bluetoothLeScanner.stopScan(leScanCallback);  //停止蓝牙扫描

搜索蓝牙服务:bluetoothGatt.discoverServices(); 

蓝牙服务特性读:搜索蓝牙服务后会有onServicesDiscovered回调,在回调中通过UUID获取所要服务,UUID可以通过FastBLE之类的工具查找,便能通过gattService.getCharacteristic获取对应特性UUID的读写功能,下面代码是打开蓝牙服务特性通知的代码

获取GATT服务getService=》获取特性getCharacteristic =》获取通知getDescriptor =》写通知描述writeDescriptor

//根据指定的服务uuid获取指定的服务
BluetoothGattService gattService = gatt.getService(UUID.fromString(mServiceUUID));
//根据指定特征值uuid获取指定的特征值A
mGattCharacteristicA = gattService.getCharacteristic(UUID.fromString(mReadCharacteristicUUID));
//获取特征值其对应的通知Descriptor
BluetoothGattDescriptor descriptor = mGattCharacteristicA.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));//这个config UUID是固定的
//写入你需要传递给外设的特征的描述值(即传递给外设的信息)
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
//通过GATT实体类将,特征值写入到外设中。成功则在 onDescriptorWrite 回调
bluetoothGatt.writeDescriptor(descriptor);
//设置特征A通知,即设备的值有变化时会通知该特征A,即回调方法onCharacteristicChanged会有该通知
                bluetoothGatt.setCharacteristicNotification(mGattCharacteristicA , true);

当特性通知时在onCharacteristicChanged回调里会相应(发送超过20个数据才会相应的),通过characteristic.getStringValue(0)获取字符串数据,之前用characteristic.getValue()获取的是特性描述的设定值,也就是这个BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE,自己试了试才找到获取BLE蓝牙发来的数据的方法。

mReadCharacteristicData = characteristic.getStringValue(0);

蓝牙服务特性写

//通过UUID获取到蓝牙特性
mWriteGattCharacteristic = gattService.getCharacteristic(UUID.fromString(mWriteCharacteristicUUID));
//蓝牙写数据
mWriteGattCharacteristic.setValue(data);
bluetoothGatt.writeCharacteristic(mWriteGattCharacteristic);

以上流程感觉还不算复杂,可是还是有不少需要的操作,比如建立service,还有广播。



价值参考:

Android Developed 官方网站 蓝牙BLE教程

Android蓝牙——手机与蓝牙设备连接及通信


问题:无法弹出要权限的弹窗,报Field requires API level 31 (current min is 24): `android.Manifest.permission#BLUETOOTH_SCAN`

解决:更改build.gradle中,将minSDK 版本改为31,再文件右上角sync即可

https://blog.csdn.net/weixin_44760073/article/details/119250705


问题:蓝牙ServiceConnection方法无法进入,报错Unable to instantiate service .....  is not accessible from ......

解决:有可能是组件没有在AndroidManifest.xml注册,参考以下连接

https://blog.csdn.net/yegucheng2618/article/details/38757519

或是从建service文件,会自动做配置,在工程栏界面右键New-》Service

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多