避免list的并发修改异常的几种方式
1、使用list的snapshot,遍历它的副本
使用如下:com.bumptech.glide.manager.ActivityFragmentLifecycle#onStart()
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
具体实现如下:
com.bumptech.glide.util.Util:
/**
* Returns a copy of the given list that is safe to iterate over and perform actions that may
* modify the original list.
*
* <p>See #303, #375, #322, #2262.
*/
@NonNull
@SuppressWarnings("UseBulkOperation")
public static <T> List<T> getSnapshot(@NonNull Collection<T> other) {
// toArray creates a new ArrayList internally and does not guarantee that the values it contains
// are non-null. Collections.addAll in ArrayList uses toArray internally and therefore also
// doesn't guarantee that entries are non-null. WeakHashMap's iterator does avoid returning null
// and is therefore safe to use. See #322, #2262.
List<T> result = new ArrayList<>(other.size());
for (T item : other) {
if (item != null) {
result.add(item);
}
}
return result;
}
2、使用CopyOnWriteArrayList
具体可以参考给ViewTreeObserver添加OnGlobalLayoutListener:
// Non-recursive listeners use CopyOnWriteArray
// Any listener invoked from ViewRootImpl.performTraversals() should not be recursive
@UnsupportedAppUsage
private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
/**
* Register a callback to be invoked when the global layout state or the visibility of views
* within the view tree changes
*
* @param listener The callback to add
*
* @throws IllegalStateException If {@link #isAlive()} returns false
*/
public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
checkIsAlive();
if (mOnGlobalLayoutListeners == null) {
mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
}
mOnGlobalLayoutListeners.add(listener);
}
/**
* Remove a previously installed global layout callback
*
* @param victim The callback to remove
*
* @throws IllegalStateException If {@link #isAlive()} returns false
*
* @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
*/
public void removeOnGlobalLayoutListener(OnGlobalLayoutListener victim) {
checkIsAlive();
if (mOnGlobalLayoutListeners == null) {
return;
}
mOnGlobalLayoutListeners.remove(victim);
}
/**
* Notifies registered listeners that a global layout happened. This can be called
* manually if you are forcing a layout on a View or a hierarchy of Views that are
* not attached to a Window or in the GONE state.
*/
public final void dispatchOnGlobalLayout() {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
if (listeners != null && listeners.size() > 0) {
CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
try {
int count = access.size();
for (int i = 0; i < count; i++) {
access.get(i).onGlobalLayout();
}
} finally {
listeners.end();
}
}
}
/**addOnGlobalLayoutListener
* Register a callback to be invoked when the global layout state or the visibility of views
* within the view tree changes
*
* @param listener The callback to add
*
* @throws IllegalStateException If {@link #isAlive()} returns false
*/
public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
checkIsAlive();
if (mOnGlobalLayoutListeners == null) {
mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
}
mOnGlobalLayoutListeners.add(listener);
}
/**
* Notifies registered listeners that a global layout happened. This can be called
* manually if you are forcing a layout on a View or a hierarchy of Views that are
* not attached to a Window or in the GONE state.
*/
public final void dispatchOnGlobalLayout() {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
if (listeners != null && listeners.size() > 0) {
CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
try {
int count = access.size();
for (int i = 0; i < count; i++) {
access.get(i).onGlobalLayout();
}
} finally {
listeners.end();
}
}
}