RadiusNetwork iBeacon Library For Android 设备发现过程梳理

来源:互联网 发布:红衣军 知乎 编辑:程序博客网 时间:2024/06/07 01:30

我所使用的时这个library的老版本,也就是0.x版。最新版是2.x了。
老版本:https://github.com/Vinayrraj/Android-iBeacon-Demo(service就是library源码,reference是例子)

使用这个library提供的例子时,发现永远只能显示我4个iBeacon设备中的1个。于是我跟了一下源码。设备发现的大概流程如下:

从这里出发

//开始计算设备距离iBeaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
  • Region是包含uuid,marjor,minor等等的一个bean

接着到startRangingBeaconsInRegion 这个方法

public void startRangingBeaconsInRegion(Region region) throws RemoteException {        Message msg = Message.obtain(null, IBeaconService.MSG_START_RANGING, 0, 0);        StartRMData obj = new StartRMData(new RegionData(region), rangingCallbackAction());        msg.obj = obj;        msg.replyTo = rangingCallback;        serviceMessenger.send(msg);    }

然后 IBeaconService.MSG_START_RANGING被执行

scanLeDevice方法被调用ScanProcessoronLeScan

 private class ScanProcessor extends AsyncTask<ScanData, Void, Void> {        @Override        protected Void doInBackground(ScanData... params) {            ScanData scanData = params[0];           IBeacon iBeacon = IBeacon.fromScanData(scanData.scanRecord, scanData.rssi);           if (iBeacon != null) {               lastIBeaconDetectionTime = new Date();               trackedBeacons.add(iBeacon);               Log.d(TAG, "iBeacon detected :"+iBeacon.getProximityUuid()+" "+iBeacon.getMajor()+" "+iBeacon.getMinor()+" accuracy: "+iBeacon.getAccuracy()+" proximity: "+iBeacon.getProximity());                                   List<Region> matchedRegions = matchingRegions(iBeacon, monitoredRegionState.keySet());               Iterator<Region> matchedRegionIterator = matchedRegions.iterator();               while (matchedRegionIterator.hasNext()) {                   Region region = matchedRegionIterator.next();                   MonitorState state = monitoredRegionState.get(region);                   if (state.markInside()) {                       state.getCallback().call(IBeaconService.this, "monitoringData", new MonitoringData(state.isInside(), region));                   }               }               Log.d(TAG, "looking for ranging region matches for this ibeacon");               matchedRegions = matchingRegions(iBeacon, rangedRegionState.keySet());               matchedRegionIterator = matchedRegions.iterator();               while (matchedRegionIterator.hasNext()) {                   Region region = matchedRegionIterator.next();                   Log.d(TAG, "matches ranging region: "+region);                   RangeState rangeState = rangedRegionState.get(region);                   rangeState.addIBeacon(iBeacon);                                }           }           //I see a device: 00:02:72:C5:EC:33 with scan data: 02 01 1A 1A FF 4C 00 02 15 84 2A F9 C4 08 F5 11 E3 92 82 F2 3C 91 AE C0 5E D0 00 00 69 C5 0000000000000000000000000000000000000000000000000000000000000000           //           // 9: proximityUuid (16 bytes) 84 2A F9 C4 08 F5 11 E3 92 82 F2 3C 91 AE C0 5E           // 25: major (2 bytes unsigned int)           // 27: minor (2 bytes unsigned int)           // 29: tx power (1 byte signed int)                      return null;        }      
private BluetoothAdapter.LeScanCallback leScanCallback =            new BluetoothAdapter.LeScanCallback() {        @Override        public void onLeScan(final BluetoothDevice device, final int rssi,                final byte[] scanRecord) {            Log.d(TAG, "got record");            new ScanProcessor().execute(new ScanData(device, rssi, scanRecord));       }    };

onLeScan是Android的内部方法,可以得到byte[] 型的scanRecord和rssi,这两个交给ScanProcessorScanProcessor调用IBeacon.fromScanData(scanData.scanRecord, scanData.rssi),将这上两个数据转为IBeacon的bean。方法是:

/**     * Construct an iBeacon from a Bluetooth LE packet collected by Android's Bluetooth APIs     *      * @param scanData The actual packet bytes     * @param rssi The measured signal strength of the packet     * @return An instance of an <code>IBeacon</code>     */    public static IBeacon fromScanData(byte[] scanData, int rssi) {        if (((int)scanData[5] & 0xff) == 0x4c &&            ((int)scanData[6] & 0xff) == 0x00 &&            ((int)scanData[7] & 0xff) == 0x02 &&            ((int)scanData[8] & 0xff) == 0x15) {                        // yes!  This is an iBeacon             // 可以确定是一个iBeacon设备         }        else if (((int)scanData[5] & 0xff) == 0x2d &&                ((int)scanData[6] & 0xff) == 0x24 &&                ((int)scanData[7] & 0xff) == 0xbf &&                ((int)scanData[8] & 0xff) == 0x16) {                // this is an Estimote beacon            //可以确定是一个Estimote厂商的beacon设备            IBeacon iBeacon = new IBeacon();            iBeacon.major = 0;            iBeacon.minor = 0;            iBeacon.proximityUuid = "00000000-0000-0000-0000-000000000000";            iBeacon.txPower = -55;            return iBeacon;        }               else {            // This is not an iBeacon            Log.d(TAG, "This is not an iBeacon advertisment.  The bytes I see are: "+bytesToHex(scanData));            return null;        }        IBeacon iBeacon = new IBeacon();        iBeacon.major = (scanData[25] & 0xff) * 0x100 + (scanData[26] & 0xff);        iBeacon.minor = (scanData[27] & 0xff) * 0x100 + (scanData[28] & 0xff);        iBeacon.txPower = (int)scanData[29]; // this one is signed        iBeacon.rssi = 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, 9, proximityUuidBytes, 0, 16);         String hexString = bytesToHex(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.proximityUuid = sb.toString();        return iBeacon;    }

问题来了

上面这段代码中可以看到,对蓝牙扫描到的byte[]型的scanData数据解析是需要手动解析的,解析的规则各个厂家可能不一样。这断代码的作者对2种规则进行了解析,一种是标准iBeacon设备(应该是某协会或论坛的标准),另一种是Estimote公司的设备。如果你用了这个library,但自己的设备不能被找到,问题应该就出在这了。解决的方法就是去找到厂家的scanData的解析规则,然后解析。可以参考这个例子:Correct layout to detect Kontakt Beacon on Android with AltBeacon

在2.x版本的library上解决这个问题

以上问题在0.x版本上如何解决我没有尝试过,但是原理就是我上面所说的。在2.x版上,这个问题有比较好的解决方法:给她 beaconManager的BeaconParsers()添加一个BeaconLayout。

 beaconManager.getBeaconParsers().add(new BeaconParser().               setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));//这个layout经过我的测试,原本2.x版本一个都找不出来的iBeacon设备全都找出来了,设备是我在淘宝买的,应该是标准解析方式。

可以参考这两个例子:

  1. Correct layout to detect Kontakt Beacon on Android with AltBeacon
  2. Is this the correct layout to detect iBeacons with AltBeacon’s Android Beacon Library?
0 0
原创粉丝点击