如何在Android应用中读取Nordic nRF52840的扩展广播数据
读取Nordic nRF52840扩展广播数据的问题
我正在开发基于Nordic nRF52840的项目,该设备正在广播扩展广告。已成功发现目标设备,但无法将扩展广播数据输出到日志中,打印相关数据时仅显示{}。请问原因是什么,以及如何正确读取扩展广播数据?
我的Android代码
package com.example.tryble_scanner; import androidx.appcompat.app.AppCompatActivity; import android.Manifest; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.util.SparseArray; import android.view.View; import android.widget.Button; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private BluetoothAdapter mBluetoothAdapter = null; private BluetoothLeScanner mBluetoothLeScanner = null; public static final int REQUEST_BT_PERMISSIONS = 0; public static final int REQUEST_BT_ENABLE = 1; private boolean mScanning = false; private Handler mHandler = null; private ScanCallback mLeScanCallBack1 = new ScanCallback() { @Override public void onScanResult(int callbackType, final ScanResult result) { String data = ConverttoString(result.getScanRecord().getManufacturerSpecificData()); BluetoothDevice btdevice = result.getDevice(); Log.d("BLE", btdevice.getAddress()); Log.d("BLE", data); } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); Log.d("BLE", "error"); } }; private ScanCallback mLeScanCallBack2 = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { Log.d("BLE", "scan stop"); } @Override public void onScanFailed(int errorCode) { Log.d("BLE", "stop scan failed"); } }; private BluetoothAdapter.LeScanCallback mLeScanCallBack3 = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnScan = findViewById(R.id.btnScan); BluetoothManager bluetoothManager = getSystemService(BluetoothManager.class); mBluetoothAdapter = bluetoothManager.getAdapter(); mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); this.mHandler = new Handler(); } public void stop_scan(View view) { Log.d("Ble", "scan stop pressed"); mBluetoothAdapter.getBluetoothLeScanner().stopScan(mLeScanCallBack2); } public void onBtnScan(View view) { Log.i("Btn", "get click"); checkBTPermission(); String[] names = new String[]{"Auden test"}; List<ScanFilter> filters = null; if (names != null) { filters = new ArrayList<>(); for (String name : names) { ScanFilter filter = new ScanFilter.Builder().setDeviceName(name).build(); filters.add(filter); } } ScanSettings scanSettings = new ScanSettings.Builder() .setLegacy(false) .build(); if (mBluetoothLeScanner == null) { Log.i("BLE", "could not get scanner"); } else { mBluetoothLeScanner.startScan(filters, scanSettings, mLeScanCallBack1); } } private void checkBTPermission() { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { int pc = this.checkSelfPermission("Manifest.permission.ACCESS_FINE_LOCATION"); pc += this.checkSelfPermission("Manifest.permission.ACCESS_COARSE_LOCATION"); if (pc != 0) { this.requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1001); } else { Log.d("BLE", "checkBT permission"); } } } public static String ConverttoString(SparseArray<byte[]> array) { if (array == null) { return "null"; } if (array.size() == 0) { return "{}"; } StringBuilder buffer = new StringBuilder(); buffer.append('{'); for (int i = 0; i < array.size(); ++i) { buffer.append(array.keyAt(i)).append("=").append(Arrays.toString(array.valueAt(i))); } buffer.append('}'); Log.d("convert", buffer.toString()); return buffer.toString(); } }
问题原因
- 数据字段选择错误:仅读取
getManufacturerSpecificData(),但扩展广播的数据可能存储在服务数据、原始广播字节数组等其他字段中,而非厂商特定数据段。 - 扫描配置不完整:仅设置
setLegacy(false)开启扩展扫描,但缺少扫描模式、物理层支持等关键配置,导致部分扩展广播数据未被解析。 - 权限不足:Android 12及以上版本需要
BLUETOOTH_SCAN权限,当前权限检查未覆盖该权限,可能限制数据获取。 - 代码依赖缺失:
ConverttoString方法使用Arrays.toString()但未导入java.util.Arrays,会导致编译或运行错误。
解决方法
1. 补充权限配置
在AndroidManifest.xml中添加适配不同版本的蓝牙权限:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
更新权限检查逻辑:
private void checkBTPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { int pc = checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN); if (pc != 0) { requestPermissions(new String[]{Manifest.permission.BLUETOOTH_SCAN}, 1001); } } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { int pc = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION); pc += checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION); if (pc != 0) { requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1001); } } }
2. 完整读取广播数据
修改扫描回调,读取所有可能的广播数据字段:
import java.util.Arrays; import java.util.Map; import android.os.ParcelUuid; // ... private ScanCallback mLeScanCallBack1 = new ScanCallback() { @Override public void onScanResult(int callbackType, final ScanResult result) { BluetoothDevice btdevice = result.getDevice(); Log.d("BLE", "设备地址: " + btdevice.getAddress()); ScanRecord record = result.getScanRecord(); if (record != null) { // 读取厂商特定数据 SparseArray<byte[]> manufacturerData = record.getManufacturerSpecificData(); Log.d("BLE", "厂商特定数据: " + ConverttoString(manufacturerData)); // 读取服务数据 Map<ParcelUuid, byte[]> serviceData = record.getServiceData(); if (!serviceData.isEmpty()) { for (Map.Entry<ParcelUuid, byte[]> entry : serviceData.entrySet()) { Log.d("BLE", "服务UUID: " + entry.getKey() + ", 数据: " + Arrays.toString(entry.getValue())); } } // 读取原始广播字节数组 byte[] rawData = record.getBytes(); Log.d("BLE", "原始广播数据: " + Arrays.toString(rawData)); // 读取设备名称 Log.d("BLE", "设备名称: " + record.getDeviceName()); } } @Override public void onScanFailed(int errorCode) { Log.d("BLE", "扫描失败,错误码: " + errorCode); } };
3. 完善扫描配置
确保ScanSettings支持扩展广播的所有特性:
ScanSettings scanSettings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) .setLegacy(false) .setPhy(ScanSettings.PHY_LE_ALL_SUPPORTED) .build();
4. 修复代码依赖
在代码顶部添加Arrays类的导入:
import java.util.Arrays;
内容的提问来源于stack exchange,提问作者JayLin1119
相关产品推荐
相关产品推荐

