在Android6.0以后要扫描蓝牙设备,还需要请求位置权限:
位置权限属于危险权限,因此需要动态获取:
//判断是否有权限
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//请求权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);
//判断是否需要 向用户解释,为什么要申请该权限
if(ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_CONTACTS)) {
Toast.makeText(this,"shouldShowRequestPermissionRationale",
Toast.LENGTH_SHORT).show();
}
}
//权限申请结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[]
grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
在实际开发的过程中你也可以设置只有支持蓝牙的设备才能运行该APP:
这里设置required为true就是只有在支持蓝牙的设备上才能运行,但是如果想让你的app提供给那些不支持BLE的设备,需要在manifest中包括上面代码并设置required="false"
,然后在运行时可以通过使用PackageManager.hasSystemFeature()
确定BLE的可用性。
// 使用此检查确定BLE是否支持在设备上,然后你可以有选择性禁用BLE相关的功能
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
Android官方提供的蓝牙扫描方式有三种,分别是:
startDiscovery()方法在大多数手机上是可以同时发现经典蓝牙和低功耗蓝牙(BLE)的,但是startDiscovery()的回调无法返回BLE的广播,所以无法通过广播识别设备,而且startDiscovery()扫描BLE效率比startLeScan()低很多。因此需要根据具体的需求去做适配,才能更高效的搜寻蓝牙。PS: startLeScan()和startScan()有重载方法可以指定规则,参数去搜索。
市面上的蓝牙设备,现在一般都是低功耗的蓝牙设备,因此只需要用下面两种扫描方式就OK了
官方提供的事例代码正是使用了这种方式,startLeScan方法有两个:
public boolean startLeScan(LeScanCallback callback)
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback)
查看源码会发现一个参数的方法是调用了两个参数的方法,只是serviceuuid[]传入为null,也就是过滤条件为空。这里我使用的是没有过滤条件的方法:
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
...
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
...
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
//扫描到的设备信息,将字符数组转成16进制字符串
String scanRecordStr = SysConvert.bytesToHex(scanRecord);
//在这里可以做一下简单的操作,避免多操作造成性能问题
}
};
因为在扫描蓝牙是一个非常非常耗能的操作,因此一定要扫描一段时间就关闭,如果需要循环扫描,可以通过定时器实现扫描的开关操作
注意:打开和关闭所使用的回调必须是同一个,虽然关闭一个没有打开的回调没有错误返回。
我在项目中是使用startScan()扫描的,因为可以通过设置一些参数来灵活控制扫描的频率等。
同样的有三个重载方法:
public void startScan(final ScanCallback callback)
public void startScan(List
public int startScan(@Nullable List
@NonNull PendingIntent callbackIntent)
这里我以第二种为例:
/**
* 扫描设备
*/
public void scanDevices() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mProgressDialog.dismiss();
if (mBluetoothLeScanner != null) {
mBluetoothLeScanner.stopScan(scanCallback);
}
}
}, 1000);
mBluetoothLeScanner.startScan(null, createScanSetting(), scanCallback);
}
/**
* 扫描广播数据设置
*
* @return
*/
public ScanSettings createScanSetting() {
ScanSettings.Builder builder = new ScanSettings.Builder();
builder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
//builder.setReportDelay(100);//设置延迟返回时间
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
}
return builder.build();
}
/**
* 回调
*/
private ScanCallback scanCallback=new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
byte[] scanData=result.getScanRecord().getBytes();
//把byte数组转成16进制字符串,方便查看
}
@Override
public void onBatchScanResults(List results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
}
};
ScanSettings :
setScanMode:有三种模式分别是
SCAN_MODE_LOW_POWER--------耗电最少,扫描时间间隔最短
SCAN_MODE_BALANCED---------平衡模式,耗电适中,扫描时间间隔一般,我使用这种模式来更新设备状态
SCAN_MODE_LOW_LATENCY---------最耗电,扫描延迟时间短,打开扫描需要立马返回结果可以使用
setReportDelay:设置扫描返回延迟时间,一般是大于零的毫秒值
如果设置了该属性,扫描结果会以list形式在onBatchScanResults方法返回,在onScanResult中没有返回,如果不设置则在onScanResult中返回,返回的信息包含在result中,可以通过getBytes返回每一个设备信息的字符数组,getDevice获取BluetoothDevice设备,在通过字符数组和蓝牙设备对象获取你想要的信息
本人也是一个蓝牙开发新手,如果上面有什么地方有问题,请指出,共同探讨,共同进步,不胜感激!