今天在程序中出现了一个ConcurrentModificationException错误。
03-20 14:31:39.542 E/AndroidRuntime( 7761): java.util.ConcurrentModificationException
03-20 14:31:39.542 E/AndroidRuntime( 7761): at java.util.ArrayList$ArrayListIterator.remove(ArrayList.java:586)
程序的代码如下:
private final List<LoginCallBack>
mCallBackList = new ArrayList<LoginCallBack>();
public void removeCallback(LoginCallBack callBack) {
if (callBack != null && mCallBackList.contains(callBack)) {
synchronized (mCallBackList) {
if (mCallBackList.contains(callBack)) {
mCallBackList.remove(callBack);
}
}
}
}
public void addCallBack(LoginCallBack callBack) {
if (callBack != null) {
synchronized (mCallBackList) {
this.mCallBackList.add(callBack);
}
}
}
private void dealNotifyCallBack() {
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (mCallBackList) {
Iterator<LoginCallBack> iter = mCallBackList.iterator();
while (iter.hasNext()) {
LoginCallBack callBack = iter.next();
try {
callBack.callBack(LoginCallBack.CODE_SUCCESS,
(UserInfo) mUserInfo.clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
iter.remove();
}
}
}
};
runWithMainThread(runnable);
}
这里有两个问题,
迭代器Iterator的remove()、hasNext()、next()与ArrayList的remove()冲突问题
synchronized同步锁的问题。callback里又调用了removeCallback方法
解决方法:Collections.synchronizedList() 无效
CopyOnWriteArrayList能解决,可以去掉synchronized关键字
为了分析问题,编写demo代码如下:
package aidl.example.caiwei.aidlclient;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Created by caiwei on 17/3/20.
*/
public class TestActivity extends AppCompatActivity {
private final Handler mUiHandler = new Handler(Looper.getMainLooper());
final List<MyCallBack> list = new CopyOnWriteArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
add(callBack1);
add(callBack2);
mUiHandler.postDelayed(new Runnable() {
@Override
public void run() {
call();
}
}, 2000);
}
private MyCallBack callBack1 = new MyCallBack(1) {
@Override
public void callback() {
continueTo(code);
}
};
private MyCallBack callBack2 = new MyCallBack(2) {
@Override
public void callback() {
continueTo(code);
}
};
private void continueTo(int code) {
Log.i("info", "start continueTo code " + code);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("info", "end continueTo code " + code);
remove(callBack1);
remove(callBack2);
}
private void add(MyCallBack callBack) {
list.add(callBack);
Log.i("info", "add callBack " + callBack.code);
}
private void remove(MyCallBack callBack) {
if (list.contains(callBack)) {
list.remove(callBack);
Log.i("info", "remove callBack " + callBack.code);
}
}
private void call() {
Runnable runnable = new Runnable() {
@Override
public void run() {
Iterator<MyCallBack> iter = list.iterator();
while (iter.hasNext()) {
MyCallBack callBack = iter.next();
callBack.callback();
Log.i("info", "call remove callBack " + callBack.code);
}
Log.i("info", "list.clear()");
list.clear();
}
};
runWithMainThread(runnable);
}
private void runWithMainThread(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run();
} else {
this.mUiHandler.post(runnable);
}
}
abstract class MyCallBack {
public int code;
public MyCallBack(int code) {
this.code = code;
}
public abstract void callback();
}
}