START EDIT 请向下滚动查看更新后的代码 END OF EDIT
我已经google并搜索了为什么会发生这种异常的原因,我知道这是因为某个对象正在读取一个列表,同时一个项目已从列表中删除。
我已根据我发现的建议相应地更改了我的代码,但有时我仍然会遇到此异常,而且我的应用程序崩溃了。它看起来是随机的,我尝试复制异常,90%的时间我没有得到异常,并且不总是遵循相同的程序,这使得调试变得困难。
我使用观察者模式。有时会使用unregister
方法,有些时间使用register
,其他时间使用notify
的方法...它会随机发生。< / p>
我使用android asynctask
从我的服务器下载几个字节,观察者模式是在需要时更新GUI。
这是我的代码:
@Override
public void register(final Observer newObserver) {
Log.d(TAG, "(Register) Observer registred: " + newObserver.toString());
observers.add(newObserver);
Log.d(TAG, "(Register) Number of registered observers: " + observers.size());
}
@Override
public void unregister(final Observer observer) {
int indexObersver = observers.indexOf(observer);
// Avoid java.util.ConcurrentModificationException
// at java.util.ArrayList$ArrayListIterator.next(ArrayList.java)
if(indexObersver >= 0)
{
observers.remove(indexObersver);
Log.d(TAG, "(Unregister) Unregistered Observer: " + observer.toString());
Log.d(TAG, "(Unregister) Now we have: " + observers.size() + " observers");
}
else
{
Log.d(TAG, "(Unregister) Registered Observer not found");
}
}
@Override
public void notifyObserverNewLocalBackup(BackupInfo backupInfo) {
// Avoid java.util.ConcurrentModificationException
// at java.util.ArrayList$ArrayListIterator.next(ArrayList.java)
for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; )
// for(Observer observer : observers)
{
Observer observer = it.next();
observer.notifyNewLocalBackup(backupInfo);
}
}
@Override
public void notifyObserverNewRemoteBackup(ArrayList<PhoneBackup> phoneBackups) {
// Avoid java.util.ConcurrentModificationException
// at java.util.ArrayList$ArrayListIterator.next(ArrayList.java)
// for(Observer observer : observers)
for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; )
{
Observer observer = it.next();
observer.notifyNewRemoteBackup(phoneBackups);
}
}
@Override
public void notifyObserverDownloadCompleted(PhoneBackup phoneBackup) {
// Avoid java.util.ConcurrentModificationException
// at java.util.ArrayList$ArrayListIterator.next(ArrayList.java)
// for(Observer observer : observers)
for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; )
{
Observer observer = it.next();
observer.notifyDownloadCompleted(phoneBackup);
}
}
@Override
public void notifyObserverUploadCompleted(boolean isSucccess) {
// Avoid java.util.ConcurrentModificationException
// at java.util.ArrayList$ArrayListIterator.next(ArrayList.java)
// for(Observer observer : observers)
for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; )
{
Observer observer = it.next();
observer.notifyUploadCompleteted(isSucccess);
}
}
现在我上次在第notifyObserverNewRemoteBackup
行的Observer observer = it.next();
方法上得到了这个摘要
06-12 04:31:58.394: W/dalvikvm(31358): threadid=1: thread exiting with uncaught exception (group=0x418fcce0)
06-12 04:31:58.629: E/AndroidRuntime(31358): FATAL EXCEPTION: main
06-12 04:31:58.629: E/AndroidRuntime(31358): Process: com.mypackage.android.design.appdesgin, PID: 31358
06-12 04:31:58.629: E/AndroidRuntime(31358): java.util.ConcurrentModificationException
06-12 04:31:58.629: E/AndroidRuntime(31358): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
06-12 04:31:58.629: E/AndroidRuntime(31358): at com.mypackage.android.design.appdesgin.asynctasks.ObserverSubjectManager.notifyObserverNewRemoteBackup(ObserverSubjectManager.java:99)
06-12 04:31:58.629: E/AndroidRuntime(31358): at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$1.success(BackupsHandler.java:318)
06-12 04:31:58.629: E/AndroidRuntime(31358): at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$1.success(BackupsHandler.java:1)
06-12 04:31:58.629: E/AndroidRuntime(31358): at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
06-12 04:31:58.629: E/AndroidRuntime(31358): at android.os.Handler.handleCallback(Handler.java:733)
06-12 04:31:58.629: E/AndroidRuntime(31358): at android.os.Handler.dispatchMessage(Handler.java:95)
06-12 04:31:58.629: E/AndroidRuntime(31358): at android.os.Looper.loop(Looper.java:136)
06-12 04:31:58.629: E/AndroidRuntime(31358): at android.app.ActivityThread.main(ActivityThread.java:5081)
06-12 04:31:58.629: E/AndroidRuntime(31358): at java.lang.reflect.Method.invokeNative(Native Method)
06-12 04:31:58.629: E/AndroidRuntime(31358): at java.lang.reflect.Method.invoke(Method.java:515)
06-12 04:31:58.629: E/AndroidRuntime(31358): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
06-12 04:31:58.629: E/AndroidRuntime(31358): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
06-12 04:31:58.629: E/AndroidRuntime(31358): at dalvik.system.NativeStart.main(Native Method)
----------------------编辑--------------------- ----------
我已经按照Anubian Noob的建议,我实施了一个同步列表,但我仍然得到例外。
这是我的更新代码:
// Singleton
public synchronized static ObserverSubjectManager getInstance()
{
if(instance == null)
{
instance = new ObserverSubjectManager();
return instance;
}
return instance;
}
private ObserverSubjectManager()
{
// observers = new ArrayList<>();
observers = Collections.synchronizedList(new ArrayList<Observer>());
}
@Override
public void register(final Observer newObserver) {
Log.d(TAG, "(Register) Observer registred: " + newObserver.toString());
synchronized (observers) {
observers.add(newObserver);
}
Log.d(TAG, "(Register) Number of registered observers: " + observers.size());
}
@Override
public void unregister(final Observer observer) {
synchronized (observers)
{
int indexObersver = observers.indexOf(observer);
if(indexObersver >= 0)
{
observers.remove(indexObersver);
Log.d(TAG, "(Unregister) Unregistered Observer: " + observer.toString());
Log.d(TAG, "(Unregister) Now we have: " + observers.size() + " observers");
}
else
{
Log.d(TAG, "(Unregister) Registered Observer not found");
}
}
}
@Override
public void notifyObserverNewLocalBackup(final BackupInfo backupInfo) {
synchronized (observers)
{
for(Observer observer : observers)
{
observer.notifyNewLocalBackup(backupInfo);
}
}
}
@Override
public void notifyObserverNewRemoteBackup(final ArrayList<PhoneBackup> phoneBackups) {
synchronized (observers)
{
for(Observer observer : observers)
{
observer.notifyNewRemoteBackup(phoneBackups);
}
}
}
@Override
public void notifyObserverDownloadCompleted(final PhoneBackup phoneBackup) {
synchronized (observers)
{
for(Observer observer : observers)
{
observer.notifyDownloadCompleted(phoneBackup);
}
}
}
@Override
public void notifyObserverUploadCompleted(final boolean isSucccess) {
synchronized (observers)
{
for(Observer observer : observers)
{
observer.notifyUploadCompleteted(isSucccess);
}
}
}
堆栈跟踪:
06-12 05:12:49.359: W/dalvikvm(31735): threadid=1: thread exiting with uncaught exception (group=0x418fcce0)
06-12 05:12:49.426: E/AndroidRuntime(31735): FATAL EXCEPTION: main
06-12 05:12:49.426: E/AndroidRuntime(31735): Process: com.mypackage.android.design.appdesgin, PID: 31735
06-12 05:12:49.426: E/AndroidRuntime(31735): java.util.ConcurrentModificationException
06-12 05:12:49.426: E/AndroidRuntime(31735): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
06-12 05:12:49.426: E/AndroidRuntime(31735): at com.mypackage.android.design.appdesgin.asynctasks.ObserverSubjectManager.notifyObserverDownloadCompleted(ObserverSubjectManager.java:126)
06-12 05:12:49.426: E/AndroidRuntime(31735): at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$2.success(BackupsHandler.java:336)
06-12 05:12:49.426: E/AndroidRuntime(31735): at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$2.success(BackupsHandler.java:1)
06-12 05:12:49.426: E/AndroidRuntime(31735): at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
06-12 05:12:49.426: E/AndroidRuntime(31735): at android.os.Handler.handleCallback(Handler.java:733)
06-12 05:12:49.426: E/AndroidRuntime(31735): at android.os.Handler.dispatchMessage(Handler.java:95)
06-12 05:12:49.426: E/AndroidRuntime(31735): at android.os.Looper.loop(Looper.java:136)
06-12 05:12:49.426: E/AndroidRuntime(31735): at android.app.ActivityThread.main(ActivityThread.java:5081)
06-12 05:12:49.426: E/AndroidRuntime(31735): at java.lang.reflect.Method.invokeNative(Native Method)
06-12 05:12:49.426: E/AndroidRuntime(31735): at java.lang.reflect.Method.invoke(Method.java:515)
06-12 05:12:49.426: E/AndroidRuntime(31735): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
06-12 05:12:49.426: E/AndroidRuntime(31735): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
06-12 05:12:49.426: E/AndroidRuntime(31735): at dalvik.system.NativeStart.main(Native Method)
答案 0 :(得分:6)
要跟进@ Rogue的评论,我会查找任何notify
(notifyDownloadCompleted()
等)回调实现取消注册观察者的实例。很容易发生的是:
1)您正在迭代一个集合。在该迭代中,您可以在其中一个已注册的观察者上调用方法。
2)注册观察者在notify
回调中,通过取消注册本身进一步通知。
3)由于您仍处于该迭代循环中,这将导致ConcurrentModificationException
,因为您无法在迭代时修改集合。
您可以通过执行反向循环来解决此问题:
for (int i = collection.size() - 1; i >= 0; i--) {
collection.get(i).notifyDownloadCompleted();
}
虽然你可以在技术上仍然遇到一些边缘情况,但也不例外。
答案 1 :(得分:3)
问题是您正在从另一个线程访问ArrayList
,这意味着当您修改它时,您将获得该异常。一个简单的解决方法是将ArrayList
替换为CopyOnWriteArrayList
(速度要慢得多),或使用Collections.synchronizedList()
。
制作同步列表:
List<Observer> list = Collection.synchronizedList(new ArrayList<Observer>);
答案 2 :(得分:2)
如果您没有从多个线程访问该集合,但只想在迭代时更改集合时避免出现问题,那么最简单的方法可能是迭代您的集合副本:
for (Observer observer : new ArrayList<>(observers)) {
observer.notifyNewLocalBackup(backupInfo);
}
这当然意味着创建副本需要一定的开销。
您也可以使用CopyOnWriteArrayList
,它涵盖并发线程访问的情况。
答案 3 :(得分:0)
在for / foreach循环中使用Iterator
List<String> stringArrayList = new ArrayList<>();
for (Iterator<String> stringIterator = stringArrayList.iterator();
stringIterator.hasNext(); ) {
String string = stringIterator.next();
if (string.equalsIgnoreCase("otherString")) {
stringIterator.remove();
}
}
P.S。您可以使用此lambda表达式简化上面的代码
stringArrayList.removeIf(string -> string.equalsIgnoreCase("otherString"));