START EDIT ,请向下滚动以获取更新的代码 END OF EDIT
我已经谷歌搜索并搜索了为什么会发生这个异常,我知道它是由一个对象正在读取列表引起的,同时从列表中删除了一个项目。
我已经根据我发现的建议更改了我的代码,但有时我仍然遇到此异常并且它使我的应用程序崩溃。它看起来是随机的,我尝试复制异常,并且 90% 的时间我没有得到异常并且并不总是遵循相同的过程,这使得调试变得困难。
我正在使用观察者模式。有时它发生在 unregister
方法中,有时发生在 register
,其他时候发生在 notify
方法中……它发生的位置非常随机。
我正在使用 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)
最佳答案
为了跟进@Rogue 的评论,我会查找任何 notify
( notifyDownloadCompleted()
等)回调实现取消注册观察者的任何实例。容易发生的是:
1)您正在迭代一个集合。在该迭代中,您在其中一个注册的观察者上调用一个方法。
2) 注册的观察者,在 notify
回调中,调用通过进一步通知取消注册自己。
3) 由于您仍处于该迭代循环中,这将导致 ConcurrentModificationException
,因为您无法在迭代时修改集合。
您可以通过执行反向循环来解决此问题:
for (int i = collection.size() - 1; i >= 0; i--) {
collection.get(i).notifyDownloadCompleted();
}
尽管从技术上讲,您仍然可以在那里遇到一些边缘情况,但也不异常(exception)。
关于java.util.ConcurrentModificationException - ArrayList,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24188624/