You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 13:15:28