程序界面 在搞安卓蓝牙通讯前,我把安卓的知识都忘光了,这次也再次快速入门了安卓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教程 问题:无法弹出要权限的弹窗,报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 |
|