干活分享:Android BLE 框架,简单易用,可能是迄今为止功能最全面的
https://github.com/a1anwang/okble
iBeacon系列文章:
Android BLE-iBeacon系列(一)iBeacon介绍
Android BLE-iBeacon系列(二)扫描识别iBeacon设备
Android BLE-iBeacon系列(三)iBeacon区域介绍(待续)
Android BLE-iBeacon系列(四)iBeacon区域的进入和退出监听(待续)
Android BLE-iBeacon系列(五)手机模拟为iBeacon(待续)
正文
扫描
iBeacon设备就是一个BLE设备,所以扫描方法和BLE扫描方法一样的:
bluetoothAdapter.startLeScan( leScanCallback);
private LeScanCallback leScanCallback=new LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice bluetoothdeivce, int rssi, byte[] scandata) {
//把byte数组转成16进制字符串,方便查看
Log.e("TAG","scandata:"+ Bytes2HexString(scandata) +" rssi:"+rssi);
}
};
//这里使用的android 4.3的API,android 5.0开始扫描API做了一点变化,为了获得更大的兼容性,我们依然使用的4.3的API。
//不需要兼容4.3的推荐使用android 5.0 的API,官方已经帮助我们封装了很多,使用起来更简单
这样扫描到的是所有的BLE设备,接下来要识别BLE设备是不是iBeacon设备
识别
识别就是通过扫描回调里的byte[] scandata来进行判断,判断依据就是苹果公司规定的iBeacon的广播格式:
iBeacon的广播格式
这个广播格式规定了iBeacon的广播数据是30个字节,第0,1,2字节是BLE4.0的协议固定格式,第3字节 length ,0x1A(16进制的1A等于10进制26)表示后面的数据是26个字节长度(3+1+26=30没毛病)。
再看第4字节名称是Type,数据0xFF,也是BLE4.0协议里的,顾名思义,0xFF表示的是一个类型,是厂商数据类型,表示后面的25个字节就是厂商数据(Manufacturer Data,BLE设备厂商可以自定义这部分数据,Manufacturer Data 由两部分构成:厂商id和数据部分)。Manufacturer Data里的前2个字节也就是第5-6字节,是厂商的id,固定占2个字节,苹果的公司id是0x004C(苹果公司向世界蓝牙组织申请的,不同的公司有不同的id),表示这个设备使用的是苹果的协议,后面的第7-8字节是苹果规定的固定的0x0215,表示这个设备是苹果的iBeacon设备,再就是iBeacon设备的uuid,major,minor等信息了。关于BLE设备广播数据的分析,可以看这篇文章,会更加了解一些:
废话不多说了,根据这个协议,立马写出如下的判断方法:
if(scandata[5]==0x4C&&scandata[6]==0x00&&scandata[7]==0x02&&scandata[8]==0x15) {
//这是一个iBeacon设备。
//注意这里004C的判断,在广播里厂商id这2个字节的数据是颠倒的
}
这种写法完全没毛病,不过可能看来不够高大上,并且体现不了BLE广播的结构,如果之前已经看过关于BLE设备广播数据的分析,那么我们可以直接拿来用的:
//这里用了android 5.0 API扫描结果来演示
//上面提到的okble框架在4.3基础上封装的跟5.0 API一样,喜欢的可以直接用
SparseArray<byte[]> manufacturerData= scanResult.getManufacturerSpecificData();
int size_1=manufacturerData.size();
for (int i=0;i<size_1;i++){
int key=manufacturerData.keyAt(i);
byte[] value=manufacturerData.get(key);
if(key==0x004C) {//0x004c is apple company id
if(value!=null && value.length==23&& value[0]==0x02&&value[1]==0x15){
//this is an iBeacon
}
}
}
这样判断虽然看起来好像复杂了点,但是体现了BLE广播的解析方法。
如果你的项目只用到iBeacon,我还是推荐使用第一种简便判断法的,效率会高点
解析
识别到是iBeacon后,然后解析出对应的uuid,major,minor等信息。
按照上面的协议图,解析很简单,代码如下(以第一种识别方法):
byte[] uuidValue=new byte[16];
System.arraycopy(scandata, 9, uuidValue, 0, 16);
String uuid="";
String hexStr=BytesToHexString(uuidValue);
uuid=hexStr.substring(0, 8);
uuid+="-";
uuid+=hexStr.substring(8, 12);
uuid+="-";
uuid+=hexStr.substring(12, 16);
uuid+="-";
uuid+=hexStr.substring(16, 20);
uuid+="-";
uuid+=hexStr.substring(20, 32);
int major=buildUint16(scandata[25], scandata[26]);
int minor=buildUint16(scandata[27], scandata[28]);
int measuredPower= scandata[29];
-------↓↓↓↓↓↓工具类↓↓↓↓↓↓↓----------
private final static byte[] hex = "0123456789ABCDEF".getBytes();
/**
* 字节数组转十六进制字符串
*
* @param b: byte[] bytes_1=new byte[]{(byte) 0xA0,(byte) 0xB1,2}
* @return "A0B102"
*/
public static String BytesToHexString(byte[] b) {
if(b==null){
return null;
}
byte[] buff = new byte[2 * b.length];
for (int i = 0; i < b.length; i++) {
buff[2 * i] = hex[(b[i] >> 4) & 0x0f];
buff[2 * i + 1] = hex[b[i] & 0x0f];
}
return new String(buff);
}
/**
* 高低位 组成int,
* @param hi
* @param lo
* @return
*/
public static int buildUint16(byte hi, byte lo) {
return (int) ((hi << 8) + (lo & 0xff));
}
封装
使用起来的话,稍微封装一下会更好用,
直接用上面的OKBLE框架可以不用看下面的了;return;
public class BeaconManager {
BluetoothAdapter bluetoothAdapter;
private BeaconListener beaconListener;
public void setBeaconListener(BeaconListener beaconListener) {
this.beaconListener = beaconListener;
}
public BeaconManager(Context context) {
BluetoothManager bluetoothManager=(BluetoothManager)context. getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter= bluetoothManager.getAdapter();
}
public void startScanBeacon(){
bluetoothAdapter.stopLeScan(leScanCallback);
bluetoothAdapter.startLeScan(leScanCallback);
}
public void stopScan(){
bluetoothAdapter.stopLeScan(leScanCallback);
}
private LeScanCallback leScanCallback=new LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice bluetoothdeivce, int rssi, byte[] scandata) {
if(scandata[5]==0x4C&&scandata[6]==0x00&&scandata[7]==0x02&&scandata[8]==0x15) {
//这是一个iBeacon设备。
//注意这里004C的判断,在广播里厂商id这2个字节的数据是颠倒的
...//这里是解析uuid,major,minor代码,省略
Beacon beacon=new Beacon(uuid,major,minor);
if(beaconListener!=null){
beaconListener.onScanBeacon(beacon);
}
}
}
};
public interface BeaconListener{
public void onScanBeacon(Beacon beacon);
}
}
public class Beacon{
public int major;
public int minor;
public String uuid;
public Beacon(String uuid, int major, int minor){
this.uuid=uuid;
this.major=major;
this.minor=minor;
}
@Override
public String toString() {
return " uuid:"+uuid+" major:"+major+" minor:"+minor;
}
}
使用
public class MainActivity extends AppCompatActivity implements BeaconManager.BeaconListener {
BeaconManager beaconManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
beaconManager = new BeaconManager(this);
beaconManager.setBeaconListener(this);
}
public void scanAction(View v) {
beaconManager.startScanBeacon();
}
//扫描回调
@Override
public void onScanBeacon(Beacon beacon) {
Log.e(TAG, " beacon设备:" + beacon.toString());
}
}
网友评论