Android 10应用扫描Nordic NRF52840失败及相关问题咨询
Android 10 BLE扫描NRF52840问题排查与修复
针对你遇到的三个问题,逐个给出原因和修复方案:
一、无法发现目标NRF52840设备
原因
- 扫描过滤条件和NRF52840的广播名称不匹配(大小写、空格都要完全一致)
- Android 10+的BLE权限逻辑变更,原权限检查未覆盖
BLUETOOTH_SCAN等新权限 - 未配置
ScanSettings,默认扫描模式可能无法捕获周期性广播
修复
- 确认NRF52840的广播名称和代码中的过滤名称完全一致
- 升级权限检查逻辑,适配Android 10+的新权限要求
- 添加低延迟扫描模式的
ScanSettings,适配周期性广播
二、无法终止扫描
原因
停止扫描时使用的ScanCallback实例和启动扫描时的不是同一个,Android要求必须使用同一个回调对象才能停止扫描
修复
停止扫描时传入启动扫描用的mLeScanCallback,而非新建的mLeScanCallback2
三、调用getName()崩溃
原因
Android 10+中未获取BLUETOOTH_CONNECT权限时调用getName()会抛出SecurityException,且未对设备名称为空的情况做判空处理
修复
- 申请
BLUETOOTH_CONNECT权限 - 调用
getName()前先判空,并捕获权限异常
修改后的完整代码
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.tryble_scanner"> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:maxSdkVersion="30"/> <uses-feature android:name="android.hardware.bluetooth" android:required="true"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.TryBle_scanner"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
MainActivity.java
package com.example.tryble_scanner; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; 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.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.View; import android.widget.Toast; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private BluetoothAdapter mBluetoothAdapter = null; private BluetoothLeScanner mBluetoothLeScanner = null; private boolean mScanning = false; private Handler mHandler = null; private static final int REQUEST_ALL_PERMISSIONS = 1001; private final ScanCallback mLeScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, final ScanResult result) { BluetoothDevice btdevice = result.getDevice(); Log.d("BLE", "设备地址:" + btdevice.getAddress()); // 安全获取设备名称 try { String deviceName = btdevice.getName(); if (deviceName != null) { Log.d("BLE", "设备名称:" + deviceName); } else { Log.d("BLE", "设备名称为空"); } } catch (SecurityException e) { Log.e("BLE", "获取名称失败:权限不足", e); Toast.makeText(MainActivity.this, "需要BLUETOOTH_CONNECT权限获取设备名称", Toast.LENGTH_SHORT).show(); } } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); Log.d("BLE", "扫描失败,错误码:" + errorCode); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BluetoothManager bluetoothManager = getSystemService(BluetoothManager.class); mBluetoothAdapter = bluetoothManager.getAdapter(); mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); this.mHandler = new Handler(); } public void stop_scan(View view) { Log.d("Ble", "停止扫描按钮点击"); if (mScanning && mBluetoothLeScanner != null) { mBluetoothLeScanner.stopScan(mLeScanCallback); mScanning = false; Toast.makeText(this, "扫描已停止", Toast.LENGTH_SHORT).show(); } } public void onBtnScan(View view) { Log.i("Btn", "扫描按钮点击"); if (mScanning) { Toast.makeText(this, "正在扫描中", Toast.LENGTH_SHORT).show(); return; } // 检查所有必要权限 if (checkAllPermissions()) { startBleScan(); } else { requestAllPermissions(); } } private boolean checkAllPermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { return ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED; } else { return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED; } } private void requestAllPermissions() { List<String> permissions = new ArrayList<>(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { permissions.add(Manifest.permission.BLUETOOTH_SCAN); } if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { permissions.add(Manifest.permission.BLUETOOTH_CONNECT); } } else { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { permissions.add(Manifest.permission.ACCESS_FINE_LOCATION); } if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) { permissions.add(Manifest.permission.BLUETOOTH_ADMIN); } } if (!permissions.isEmpty()) { ActivityCompat.requestPermissions(this, permissions.toArray(new String[0]), REQUEST_ALL_PERMISSIONS); } } private void startBleScan() { if (mBluetoothLeScanner == null) { Log.i("BLE", "无法获取BLE扫描器"); Toast.makeText(this, "BLE扫描器初始化失败", Toast.LENGTH_SHORT).show(); return; } // 配置扫描过滤,匹配NRF52840的广播名称 List<ScanFilter> filters = new ArrayList<>(); ScanFilter filter = new ScanFilter.Builder().setDeviceName("Auden test").build(); filters.add(filter); // 配置扫描设置,适配周期性广播 ScanSettings settings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // 低延迟模式,适合扫描周期性广播 .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) .build(); mBluetoothLeScanner.startScan(filters, settings, mLeScanCallback); mScanning = true; Toast.makeText(this, "开始扫描", Toast.LENGTH_SHORT).show(); // 可选:设置扫描超时,避免长时间扫描 mHandler.postDelayed(() -> { if (mScanning) { stop_scan(null); } }, 15000); // 15秒后自动停止 } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_ALL_PERMISSIONS) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { startBleScan(); } else { Toast.makeText(this, "权限不足,无法扫描BLE设备", Toast.LENGTH_SHORT).show(); } } } }
内容的提问来源于stack exchange,提问作者JayLin1119
相关产品推荐
相关产品推荐

