美文网首页
Android BLE-iBeacon系列(二)扫描识别iBea

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

作者: Holly_dd20 | 来源:发表于2018-07-06 18:18 被阅读843次

    干活分享: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());
        }
    }
    

    相关文章

      网友评论

          本文标题:Android BLE-iBeacon系列(二)扫描识别iBea

          本文链接:https://www.haomeiwen.com/subject/sclbuftx.html