安卓蓝牙扫描应用在虚拟设备运行正常,真机点击扫描后崩溃如何解决
安卓蓝牙扫描物理机崩溃修复方案
崩溃根因
从报错日志可以定位到空指针触发在ArrayAdapter创建列表条目阶段,核心问题是:
- 部分无名称的蓝牙设备(比如未命名的BLE设备、广播Beacon设备)调用
BluetoothDevice.getName()会返回null,你直接将null存入字符串类型的设备列表后,系统默认列表布局渲染时会调用条目的toString()方法,触发空指针异常。模拟器通常返回模拟的有名称设备,不会触发该问题,物理机扫描真实环境设备时才会出现。 - 额外潜在问题:安卓6.0以上位置权限、安卓12以上蓝牙扫描权限未正确配置、未判断权限授予状态就启动扫描,也可能引发异常。
修复方案
第一步:补全AndroidManifest.xml权限
在AndroidManifest.xml的manifest标签下添加以下权限,适配不同安卓版本的蓝牙扫描要求:
<!-- 基础蓝牙权限,安卓12及以上无需这两个 --> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <!-- 安卓12+专属蓝牙权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- 安卓6.0-11扫描需要位置权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" /> <!-- 声明应用需要蓝牙硬件,非强制要求 --> <uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />
第二步:修改MainActivity.java代码
修改后的代码如下,核心调整了空值处理、权限判断、列表清空逻辑:
package com.example.drop_java_test2; 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.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.Toast; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { Button scanBt; ListView scanList; ArrayList<String> devicesList = new ArrayList<>(); ArrayAdapter<String> arrayAdapter; BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); private static final int PERMISSION_REQUEST_CODE = 1001; private static final int REQUEST_ENABLE_BT = 1002; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); scanBt = findViewById(R.id.bt); scanList = findViewById(R.id.list); arrayAdapter = new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_list_item_1, devicesList); scanList.setAdapter(arrayAdapter); // 注册蓝牙发现广播 IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(myReciver, intentFilter); scanBt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 先判断蓝牙是否开启 if (btAdapter == null || !btAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { requestNecessaryPermissions(); return; } startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); return; } // 检查权限是否授予 if (!checkHasNecessaryPermissions()) { requestNecessaryPermissions(); return; } // 启动扫描前清空列表,避免重复添加 devicesList.clear(); arrayAdapter.notifyDataSetChanged(); // 停止之前的扫描再启动新扫描,避免冲突 if (btAdapter.isDiscovering()) { btAdapter.cancelDiscovery(); } boolean startSuccess = btAdapter.startDiscovery(); Toast.makeText(MainActivity.this, startSuccess ? "扫描启动成功" : "扫描启动失败", Toast.LENGTH_SHORT).show(); } }); } // 检查是否已经获取所有必要权限 private boolean checkHasNecessaryPermissions() { if (android.os.Build.VERSION.SDK_INT >= android.os.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; } } // 动态请求必要权限 private void requestNecessaryPermissions() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT }, PERMISSION_REQUEST_CODE); } else { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.ACCESS_FINE_LOCATION }, PERMISSION_REQUEST_CODE); } } BroadcastReceiver myReciver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device == null) return; // 设备名称为空时兜底显示MAC地址,避免空值 String deviceName = device.getName(); String showName = (deviceName == null || deviceName.trim().isEmpty()) ? "未知设备:" + device.getAddress() : deviceName + "\n" + device.getAddress(); devicesList.add(showName); arrayAdapter.notifyDataSetChanged(); } } }; @Override protected void onDestroy() { super.onDestroy(); // 销毁时注销广播、停止扫描,避免内存泄漏 unregisterReceiver(myReciver); if (btAdapter != null && ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED) { btAdapter.cancelDiscovery(); } } }
第三步:XML布局无需调整
你提供的布局文件逻辑正常,不需要修改。
内容的提问来源于stack exchange,提问作者Lukas
相关产品推荐
相关产品推荐

