使用Android Bluetooth LE API扫描BLE设备时,如何获取信标传输的URL?
解决Android BLE扫描中提取信标URL的问题
我来帮你搞定这个问题!你现在用onLeScan回调拿扫描结果,但不知道怎么提取信标的URL,其实核心是解析广播数据里的Eddystone-URL格式(这是当前最通用的信标URL传输标准),下面分原生API和第三方库两种方案给你讲:
一、原生Android Bluetooth LE API方案(优先推荐)
onLeScan里的scanRecord字节数组包含了蓝牙广播的全部数据,我们要从中筛选出Eddystone信标的服务数据(UUID为0xFEAA),再解码出URL。
1. 解析扫描记录,定位Eddystone-URL数据
先遍历扫描记录,找到对应Eddystone服务UUID的片段:
private String parseEddystoneUrl(byte[] scanRecord) { int offset = 0; while (offset < scanRecord.length) { int dataLength = scanRecord[offset] & 0xFF; if (dataLength == 0) break; offset++; int dataType = scanRecord[offset] & 0xFF; // 筛选"服务数据"类型的广播片段 if (dataType == 0x16) { // 解析服务UUID(小端序转大端) int serviceUuid = ((scanRecord[offset+1] & 0xFF) << 8) | (scanRecord[offset+2] & 0xFF); // 匹配Eddystone的服务UUID:0xFEAA if (serviceUuid == 0xFEAA) { byte frameType = scanRecord[offset+3]; // 确认是URL类型的帧(Frame Type=0x10) if (frameType == 0x10) { // URL数据从offset+5开始,长度为dataLength-4(减去UUID2字节+帧类型1字节+发射功率1字节) return decodeCompressedUrl(scanRecord, offset + 5, dataLength - 4); } } } offset += dataLength; } return null; }
2. 解码压缩后的URL
Eddystone-URL用了一套字符映射表压缩URL,我们需要把编码字节转成真实URL:
private String decodeCompressedUrl(byte[] scanRecord, int startOffset, int length) { // URL前缀映射表 String[] urlPrefixes = { "http://www.", "https://www.", "http://", "https://" }; // 特殊字符替换表 char[] specialChars = { '.', '/', ':', '-', '_', '~', '%', '+', '@', '$', ',', '!', '\'', '(', ')', '*', '&', '#', ';', '=', '?', '[', ']', '^', ' ', '\'', '"', '\\', '|', '<', '>', '`' }; StringBuilder urlBuilder = new StringBuilder(); // 先处理URL前缀 int prefixIndex = scanRecord[startOffset] & 0xFF; if (prefixIndex < urlPrefixes.length) { urlBuilder.append(urlPrefixes[prefixIndex]); } startOffset++; length--; // 处理剩余字符 for (int i = 0; i < length; i++) { byte code = scanRecord[startOffset + i]; int charCode = code & 0xFF; if (charCode <= 0x1F) { // 匹配特殊字符替换表 if (charCode < specialChars.length) { urlBuilder.append(specialChars[charCode]); } } else if (charCode >= 0x20 && charCode <= 0x7E) { // 直接转ASCII可打印字符 urlBuilder.append((char) charCode); } } return urlBuilder.toString(); }
3. 在onLeScan中调用解析
@Override public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) { String beaconUrl = parseEddystoneUrl(scanRecord); if (beaconUrl != null) { // 成功拿到URL,这里做你需要的处理 Log.d("BeaconURL", "找到URL信标: " + beaconUrl + ",RSSI: " + rssi); } }
注:如果你的信标是厂商自定义的URL格式,需要对应厂商的解析规则,但Eddystone-URL是行业通用标准,绝大多数场景适用。
二、第三方库方案(简化开发)
如果觉得原生解析太繁琐,可以用成熟的信标库AltBeacon,它支持一键解析Eddystone-URL、iBeacon等多种格式:
1. 添加依赖
在app/build.gradle中加入:
dependencies { implementation 'org.altbeacon:android-beacon-library:2.21.4' }
2. 初始化并扫描
// 初始化BeaconManager BeaconManager beaconManager = BeaconManager.getInstanceForApplication(getApplicationContext()); // 配置支持Eddystone-URL格式 beaconManager.getBeaconParsers().add(new BeaconParser() .setBeaconLayout("s:0-1=feaa,m:2-2=10,p:3-3:-41,i:4-21v")); // 设置距离扫描回调(实时获取信标数据) beaconManager.addRangeNotifier((beacons, region) -> { for (Beacon beacon : beacons) { // 直接提取URL String url = beacon.getId1().toString(); Log.d("BeaconURL", "第三方库解析到URL: " + url); } }); // 启动扫描 try { beaconManager.startRangingBeaconsInRegion(new Region("myScanRegion", null, null, null)); } catch (RemoteException e) { e.printStackTrace(); }
额外提示
- Android 5.0及以上更推荐用
BluetoothLeScanner+ScanCallback的新API,比onLeScan更灵活,还支持扫描过滤规则。 - 务必申请必要权限:Android 12+需要
BLUETOOTH_SCAN、BLUETOOTH_CONNECT,以及位置权限(ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION),否则BLE扫描会失效。
内容的提问来源于stack exchange,提问作者Yousha Arif
相关产品推荐
相关产品推荐

