向ArrayAdapter添加对象时应用崩溃,蓝牙设备ListView展示故障
蓝牙设备列表ArrayAdapter崩溃问题排查与解决
嘿,我之前做蓝牙设备扫描的学校项目时,也碰到过一模一样的情况——只要碰ArrayAdapter应用就崩!后来排查下来都是一些容易踩的Android UI开发坑,给你梳理下可能的原因和对应的解决办法:
最常见的崩溃原因
- 子线程直接操作UI:蓝牙扫描的回调(不管是经典蓝牙还是BLE扫描)都是在子线程跑的,Android规定所有UI相关操作(包括更新ArrayAdapter、刷新ListView)必须在主线程执行,直接在子线程修改Adapter必崩。
- Adapter初始化不规范:比如用了
getApplicationContext()而不是当前Activity实例当Context,或者绑定的数据源(比如ArrayList)是null就创建Adapter,又或者还没拿到ListView实例就绑定Adapter了。 - 数据源修改后未通知Adapter:虽然这个不一定会崩溃,但如果是在错误线程里修改数据源再调用
notifyDataSetChanged(),也会触发崩溃。
一步步排查修复
1. 强制在主线程更新Adapter
把蓝牙扫描回调里的设备名称添加逻辑,用runOnUiThread()包裹起来,切换到主线程操作:
// 蓝牙扫描回调中拿到设备信息后 final String deviceName = device.getName() != null ? device.getName() : "未知设备"; runOnUiThread(new Runnable() { @Override public void run() { // 往数据源里加设备名 yourDeviceList.add(deviceName); // 通知Adapter更新UI yourArrayAdapter.notifyDataSetChanged(); } });
2. 检查Adapter初始化是否正确
确保初始化时用对Context、数据源非空,并且先拿到ListView再绑定Adapter:
// 正确的初始化流程 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.your_layout); // 1. 先初始化数据源(必须非null) ArrayList<String> deviceNames = new ArrayList<>(); // 2. 拿到ListView实例 ListView deviceListView = findViewById(R.id.list_view_device); // 3. 创建Adapter,用当前Activity作为Context ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, deviceNames); // 4. 绑定Adapter到ListView deviceListView.setAdapter(adapter); }
千万别用getApplicationContext()当Adapter的Context,这会导致布局加载异常!
3. 查看崩溃日志精准定位
打开Android Studio的Logcat,搜索AndroidRuntime,找到崩溃的堆栈信息:
- 如果是
CalledFromWrongThreadException:就是线程问题,按步骤1解决。 - 如果是
NullPointerException:检查是不是ListView、Adapter或者数据源没初始化就用了。
完整简化测试代码
给你一个能跑的简化版代码,你可以对照着改自己的项目:
package z.ut010; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Intent; import java.util.ArrayList; public class BluetoothDeviceActivity extends AppCompatActivity { private ArrayList<String> deviceList; private ArrayAdapter<String> adapter; private BluetoothAdapter bluetoothAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化ListView和Adapter ListView listView = findViewById(R.id.list_view); deviceList = new ArrayList<>(); adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, deviceList); listView.setAdapter(adapter); // 初始化蓝牙适配器 bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter == null) { // 设备不支持蓝牙,提示用户 return; } // 请求开启蓝牙 if (!bluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, 1); } else { startBluetoothScan(); } } private void startBluetoothScan() { // 开始BLE扫描(经典蓝牙用startDiscovery(),回调逻辑类似) bluetoothAdapter.startLeScan(new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { final String deviceName = device.getName() != null ? device.getName() : "未知设备"; // 切换到主线程更新UI runOnUiThread(new Runnable() { @Override public void run() { // 避免重复添加同一设备 if (!deviceList.contains(deviceName)) { deviceList.add(deviceName); adapter.notifyDataSetChanged(); } } }); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 1 && resultCode == RESULT_OK) { // 用户开启蓝牙后开始扫描 startBluetoothScan(); } } }
别忘了在AndroidManifest.xml里加必要权限:
<!-- 蓝牙基础权限 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- Android 6.0+ 需要位置权限才能扫描蓝牙 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
内容的提问来源于stack exchange,提问作者Zacord
相关产品推荐
相关产品推荐

