1.前言
继低功耗蓝牙开发完后开发iBeacon,其实iBeacon搜索设备和ble开发都一样,这里我只介绍其具体的不同之处,
2.开发环境
华为p20 AndroidStudio 3.2 + 低功耗蓝牙设备
经过测试发现有的手机搜索蓝牙设备需要开启location权限,在华为上我的手机没有开启也可以进行搜索,另外还需要动态的允许权限
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.ACCESS_FINE_LOCATION)!=PackageManager.PERMISSION_GRANTED||ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.ACCESS_COARSE_LOCATION)!=PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},1);
}
3.搜索设备
和ble一样,因为iBeacon是一个协议搭载在ble设备上,在搜寻设备的时候需要先查看自己的设备是否支持蓝牙,同时检测是否打开蓝牙,当二者都满足的时候再通过不同的方式搜索ble设备
adapter=BluetoothAdapter.getDefaultAdapter();//获取本地蓝牙设备
if(adapter!=null){
if(!adapter.isEnabled()){ //判断是否开启了蓝牙,如何未开启执行开启操作
Intent intent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent);
}
}else{
//不支持蓝牙
}
获取本地的蓝牙适配器有二种方法,我以上是使用BluetoothAdapter获取默认Adapter获取到的,下面这个使用BluetoothManager再获取到,二者是一样的,当我获取到adapter之后用她获取到BluetoothLeScanner对象,将会用她来对ble设备进行扫描(这是新的扫描方式,之前通过startLeScan()方法 已被弃用)
BluetoothManager bluetoothManager=(BluetoothManager)getSystemService(BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter=bluetoothManager.getAdapter();
BluetoothLeScanner bluetoothLeScanner=bluetoothAdapter.getBluetoothLeScanner();
扫描将调用BluetoothLeScanner对象的startScan()方法,但是此对象需要传入一个回调对象————ScanCallback,创建此回调对她进行改写,当扫描到设备时就会调用此回调函数种的OnScanResult函数,从而获取到Device 改写ScanCallback写在下面,因为iBeacon解析的关键就在于此
搜索设备
bluetoothLeScanner.startScan(leCallback);
4.获取iBeacon
和传统蓝牙和低功耗蓝牙不同的是iBeacon无需连接就可以获取到iBeacon中的数据
ScanCallback leCallback=new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
//result.getScanRecord().getBytes()中存放的就是ibeacon中的信息,只需要对他进行解析即可获取到
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.d(TAG, "搜索失败");
}
};
5.iBeacon数据结构
6.解析iBeacon数据(参考其他文档)
解析iBeacon 编写了一个类用于解析
public class IBeaconClass {
static public class iBeacon implements Serializable {
public String beaconName;
public int major;
public int minor;
public String uuid;
public String bluetoothAddress;
public int txPower;
public int rssi;
public double distance;
}
public static iBeacon fromScanData(BluetoothDevice device, int rssi, byte[] scanData) {
int startByte = 2;
boolean patternFound = false;
while (startByte <= 5) {
if (((int)scanData[startByte+2] & 0xff) == 0x02 &&
((int)scanData[startByte+3] & 0xff) == 0x15) {
// yes! This is an iBeacon
patternFound = true;
break;
}
else if (((int)scanData[startByte] & 0xff) == 0x2d &&
((int)scanData[startByte+1] & 0xff) == 0x24 &&
((int)scanData[startByte+2] & 0xff) == 0xbf &&
((int)scanData[startByte+3] & 0xff) == 0x16) {
iBeacon iBeacon = new iBeacon();
iBeacon.major = 0;
iBeacon.minor = 0;
iBeacon.uuid = "00000000-0000-0000-0000-000000000000";
iBeacon.txPower = -55;
return iBeacon;
}
else if (((int)scanData[startByte] & 0xff) == 0xad &&
((int)scanData[startByte+1] & 0xff) == 0x77 &&
((int)scanData[startByte+2] & 0xff) == 0x00 &&
((int)scanData[startByte+3] & 0xff) == 0xc6) {
iBeacon iBeacon = new iBeacon();
iBeacon.major = 0;
iBeacon.minor = 0;
iBeacon.uuid = "00000000-0000-0000-0000-000000000000";
iBeacon.txPower = -55;
return iBeacon;
}
startByte++;
}
if (patternFound == false) {
// This is not an iBeacon
return null;
}
iBeacon iBeacon = new iBeacon();
iBeacon.major = (scanData[startByte+20] & 0xff) * 0x100 + (scanData[startByte+21] & 0xff);
iBeacon.minor = (scanData[startByte+22] & 0xff) * 0x100 + (scanData[startByte+23] & 0xff);
iBeacon.txPower = (int)scanData[startByte+24]; // this one is signed
iBeacon.rssi = rssi;
iBeacon.distance = calculateAccuracy(iBeacon.txPower,iBeacon.rssi);
// AirLocate:
// 02 01 1a 1a ff 4c 00 02 15 # Apple's fixed iBeacon advertising prefix
// e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon profile uuid
// 00 00 # major
// 00 00 # minor
// c5 # The 2's complement of the calibrated Tx Power
// Estimote:
// 02 01 1a 11 07 2d 24 bf 16
// 394b31ba3f486415ab376e5c0f09457374696d6f7465426561636f6e00000000000000000000000000000000000000000000000000
byte[] proximityUuidBytes = new byte[16];
System.arraycopy(scanData, startByte+4, proximityUuidBytes, 0, 16);
String hexString = bytesToHexString(proximityUuidBytes);
StringBuilder sb = new StringBuilder();
sb.append(hexString.substring(0,8));
sb.append("-");
sb.append(hexString.substring(8,12));
sb.append("-");
sb.append(hexString.substring(12,16));
sb.append("-");
sb.append(hexString.substring(16,20));
sb.append("-");
sb.append(hexString.substring(20,32));
iBeacon.uuid = sb.toString();
if (device != null) {
iBeacon.bluetoothAddress = device.getAddress();
iBeacon.beaconName = device.getName();
}
return iBeacon;
}
private static String bytesToHexString(byte[] src){
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* 估算用户设备到ibeacon的距离
*
* @param txPower
* @param rssi
* @return
*/
public static double calculateAccuracy(int txPower, double rssi) {
if (rssi == 0) {
return -1.0; // if we cannot determine accuracy, return -1.
}
double ratio = rssi * 1.0 / txPower;
if (ratio < 1.0) {
return Math.pow(ratio, 10);
} else {
double accuracy = (0.89976) * Math.pow(ratio, 7.7095) + 0.111;
return accuracy;
}
}
}