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

Android 10应用扫描Nordic NRF52840失败及相关问题咨询

Android 10 BLE扫描NRF52840问题排查与修复

针对你遇到的三个问题,逐个给出原因和修复方案:

一、无法发现目标NRF52840设备

原因

  1. 扫描过滤条件和NRF52840的广播名称不匹配(大小写、空格都要完全一致)
  2. Android 10+的BLE权限逻辑变更,原权限检查未覆盖BLUETOOTH_SCAN等新权限
  3. 未配置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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 15:15:44