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

Android ConcurrentModificationException问题:BLE扫描数据处理代码异常求助

解决Android BLE扫描中的ConcurrentModificationException问题

嘿,我来帮你搞定这个问题!你遇到的ConcurrentModificationException完全是因为在遍历List的同时修改它——也就是你在for-each循环里调用list.add(),这是Java集合框架的一个经典坑。

问题原因分析

先拆解下你的代码逻辑:你在遍历list的过程中,一旦发现当前遍历的设备和新扫描到的设备地址不匹配,就直接往列表里添加新元素。但Java的for-each循环本质是通过迭代器(Iterator)实现的,迭代器有「快速失败」机制——只要检测到集合在遍历期间被迭代器以外的方式修改(比如直接调用add()/remove()),就会抛出这个异常。

另外,BLE扫描的回调通常运行在系统工作线程,如果你同时在UI线程操作这个list,多线程并发修改会让问题更严重,但核心触发点还是遍历+修改集合的冲突。而且你的原逻辑还有个小bug:如果列表里有多个设备,只要遍历到第一个不匹配的设备就添加新元素,会导致重复添加同一个设备多次。

解决方案

下面给你三种靠谱的解决方式,按需选择:

1. 改用索引遍历List(最直接的修复)

放弃for-each循环,用基于索引的普通for循环遍历,这样不会触发迭代器的检查,同时调整逻辑先统一检查设备是否存在,再决定是否添加:

@Override 
public void bleDataReceiverObserver(BluetoothDevice bluetoothDevice) { 
    Log.v("bleDataReceiverObserver", bluetoothDevice.getName()); 
    boolean deviceExists = false;
    
    // 先遍历检查设备是否已存在
    for (int i = 0; i < list.size(); i++) {
        BleData bleData = list.get(i);
        if (bleData.getAddress().equals(bluetoothDevice.getAddress())) {
            deviceExists = true;
            break;
        }
    }
    
    // 不存在则添加
    if (!deviceExists) {
        list.add(new BleData(bluetoothDevice, bluetoothDevice.getAddress(), bluetoothDevice.getName()));
    }
}

2. 使用线程安全的CopyOnWriteArrayList

如果你的场景是多线程操作List(比如BLE回调线程+UI线程),直接用CopyOnWriteArrayList替代普通ArrayList。它会在修改时复制一份新数组,遍历用的是原数组的快照,完全避免并发修改异常,同时自带线程安全性:

// 初始化List时改成CopyOnWriteArrayList
private List<BleData> list = new CopyOnWriteArrayList<>();

@Override 
public void bleDataReceiverObserver(BluetoothDevice bluetoothDevice) { 
    Log.v("bleDataReceiverObserver", bluetoothDevice.getName()); 
    boolean deviceExists = false;
    
    for (BleData bleData : list) {
        if (bleData.getAddress().equals(bluetoothDevice.getAddress())) {
            deviceExists = true;
            break;
        }
    }
    
    if (!deviceExists) {
        list.add(new BleData(bluetoothDevice, bluetoothDevice.getAddress(), bluetoothDevice.getName()));
    }
}

这种方式适合读多写少的场景(比如BLE扫描),唯一的小缺点是修改时会复制数组,性能开销略高,但对BLE扫描来说完全可以忽略。

3. 遍历List的副本

先复制一份原List的快照,遍历副本而不是原List,这样即使原List被修改,也不会影响遍历过程:

@Override 
public void bleDataReceiverObserver(BluetoothDevice bluetoothDevice) { 
    Log.v("bleDataReceiverObserver", bluetoothDevice.getName()); 
    boolean deviceExists = false;
    
    // 复制原List的副本用于遍历
    List<BleData> tempList = new ArrayList<>(list);
    for (BleData bleData : tempList) {
        if (bleData.getAddress().equals(bluetoothDevice.getAddress())) {
            deviceExists = true;
            break;
        }
    }
    
    if (!deviceExists) {
        list.add(new BleData(bluetoothDevice, bluetoothDevice.getAddress(), bluetoothDevice.getName()));
    }
}

如果是多线程场景,建议配合synchronized锁使用,避免复制副本时原List被修改导致的数据不一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:52:38