Android BLE-iBeacon系列(二)扫描识别iBeacon设备


干货分享: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的广播格式:


Android BLE-iBeacon系列(二)扫描识别iBeacon设备_第1张图片
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 manufacturerData= scanResult.getManufacturerSpecificData();
        int size_1=manufacturerData.size();
        for (int i=0;i

这样判断虽然看起来好像复杂了点,但是体现了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 BeaconScanManager {
    
    BluetoothAdapter bluetoothAdapter;
    
    private BeaconListener beaconListener;
    
    public void setBeaconListener(BeaconListener beaconListener) {
        this.beaconListener = beaconListener;
    }
    
    public BeaconScanManager(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 BeaconScanManager.BeaconListener {
    
    BeaconScanManager beaconScanManager;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        beaconScanManager = new BeaconScanManager(this);
        beaconScanManager.setBeaconListener(this);
        
    }
    public void scanAction(View v) {
        beaconScanManager.startScanBeacon();
    }
    //扫描回调
    @Override
    public void onScanBeacon(Beacon beacon) {
        Log.e(TAG, "  beacon设备:" + beacon.toString());
    }
}

你可能感兴趣的:(Android BLE-iBeacon系列(二)扫描识别iBeacon设备)