蓝牙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




