You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

蓝牙APP设备扫描异常:仅进入系统蓝牙设置后才可扫描到设备求助

问题诊断

我之前帮好几个开发者解决过一模一样的问题,你的情况主要踩了两个蓝牙开发的常见坑:

  • 蓝牙开启的异步处理错误:你在调用startActivityForResult请求开启蓝牙后,立刻调用startDiscovery,但蓝牙开启是异步操作,这时候蓝牙大概率还没真正激活,扫描自然无法执行。
  • 权限适配不完整:你只声明了静态权限,但Android 6.0+需要动态申请位置权限,Android 12(API 31)及以上还新增了专属的蓝牙扫描/连接权限,缺少这些权限会导致扫描被系统拦截。
解决方案

下面一步步帮你修复问题:

1. 补全Manifest权限声明

先在AndroidManifest.xml里补充完整的权限,适配不同Android版本:

<!-- 基础蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Android 10+ 扫描蓝牙需要位置权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Android 12+ 专属蓝牙扫描/连接权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- 声明设备支持蓝牙 -->
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />

neverForLocation标记是告诉系统你扫描蓝牙不需要获取位置信息,避免不必要的位置权限弹窗。

2. 动态申请权限(Android 6.0+)

静态权限只是声明,必须通过动态请求让用户授权才能生效,我们用ActivityResultLauncher来处理权限请求(替代已过时的requestPermissions)。

3. 修正扫描时机

必须等蓝牙真正开启后再启动扫描,同样用ActivityResultLauncher处理蓝牙开启的回调。

修改后的完整代码

import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "bluetooth";
    private BluetoothAdapter bluetoothAdapter;
    private Button mBTEnableButton;
    private BroadcastReceiver mReceiver;
    private TextView mShowDevices;
    private ProgressBar mProgressBar;
    // 蓝牙开启请求的Launcher
    private ActivityResultLauncher<Intent> enableBtLauncher;
    // 权限请求的Launcher
    private ActivityResultLauncher<String[]> permissionLauncher;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBTEnableButton = findViewById(R.id.enable_bt_button);
        mShowDevices = findViewById(R.id.show_connected_devices);
        mProgressBar = findViewById(R.id.progressbar);
        mProgressBar.setVisibility(View.INVISIBLE);

        // 初始化蓝牙适配器
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {
            Toast.makeText(this, "设备不支持蓝牙功能", Toast.LENGTH_SHORT).show();
            finish();
            return;
        }

        // 注册蓝牙广播接收器
        mReceiver = new BlueToothReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
        registerReceiver(mReceiver, intentFilter);

        // 初始化蓝牙开启请求回调
        enableBtLauncher = registerForActivityResult(
                new ActivityResultContracts.StartActivityForResult(),
                result -> {
                    if (result.getResultCode() == RESULT_OK) {
                        // 蓝牙成功开启,启动扫描
                        startBluetoothDiscovery();
                    } else {
                        Toast.makeText(this, "蓝牙开启失败", Toast.LENGTH_SHORT).show();
                    }
                }
        );

        // 初始化权限请求回调
        permissionLauncher = registerForActivityResult(
                new ActivityResultContracts.RequestMultiplePermissions(),
                permissions -> {
                    boolean allGranted = true;
                    for (Boolean granted : permissions.values()) {
                        if (!granted) {
                            allGranted = false;
                            break;
                        }
                    }
                    if (allGranted) {
                        // 权限全部授予,检查蓝牙状态
                        checkBluetoothState();
                    } else {
                        Toast.makeText(this, "需要授予权限才能扫描蓝牙设备", Toast.LENGTH_SHORT).show();
                    }
                }
        );

        // 先请求必要权限
        requestRequiredPermissions();

        mBTEnableButton.setOnClickListener(v -> checkBluetoothState());
    }

    // 请求适配不同版本的权限
    private void requestRequiredPermissions() {
        List<String> permissions = new ArrayList<>();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            // Android 12+ 请求蓝牙扫描权限
            permissions.add(Manifest.permission.BLUETOOTH_SCAN);
        } else {
            // Android 6.0-11 请求位置权限
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
            }
        }
        if (!permissions.isEmpty()) {
            permissionLauncher.launch(permissions.toArray(new String[0]));
        } else {
            // 权限已全部授予,直接检查蓝牙状态
            checkBluetoothState();
        }
    }

    // 检查蓝牙状态,未开启则请求开启
    private void checkBluetoothState() {
        if (!bluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            enableBtLauncher.launch(enableBtIntent);
        } else {
            // 蓝牙已开启,直接启动扫描
            startBluetoothDiscovery();
        }
    }

    // 启动蓝牙扫描(先停止已有扫描)
    private void startBluetoothDiscovery() {
        if (bluetoothAdapter.isDiscovering()) {
            bluetoothAdapter.cancelDiscovery();
        }
        bluetoothAdapter.startDiscovery();
        Toast.makeText(this, "开始扫描蓝牙设备", Toast.LENGTH_SHORT).show();
    }

    private class BlueToothReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "收到广播: " + intent.getAction());
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                String deviceName = device.getName();
                String deviceAddress = device.getAddress();
                // 追加设备信息(避免覆盖之前的结果)
                String currentText = mShowDevices.getText().toString();
                String newText = currentText + "\n\n设备名称: " + (deviceName != null ? deviceName : "未知设备") + "\n设备地址: " + deviceAddress;
                mShowDevices.setText(newText);
                Log.d(TAG, "发现设备: " + deviceName + " (" + deviceAddress + ")");
            } else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
                mProgressBar.setVisibility(View.VISIBLE);
                Log.d(TAG, "扫描开始");
                // 清空之前的设备列表
                mShowDevices.setText("正在扫描蓝牙设备...");
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                mProgressBar.setVisibility(View.INVISIBLE);
                Log.d(TAG, "扫描结束");
                if (mShowDevices.getText().toString().equals("正在扫描蓝牙设备...")) {
                    mShowDevices.setText("未发现任何蓝牙设备");
                }
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mReceiver);
        // 销毁时停止扫描,节省电量
        if (bluetoothAdapter != null && bluetoothAdapter.isDiscovering()) {
            bluetoothAdapter.cancelDiscovery();
        }
    }
}

额外注意事项

  • 蓝牙扫描会消耗较多电量,扫描完成后记得调用cancelDiscovery()停止扫描。
  • 部分Android 10+设备需要开启位置服务才能扫描到蓝牙设备,如果还是扫不到,可以提示用户开启位置服务。
  • 确保周围的蓝牙设备处于可被发现状态(部分设备默认隐藏,需要手动设置为可发现)。

内容的提问来源于stack exchange,提问作者amit kumar

火山引擎 最新活动