Loading core/java/android/view/ViewTreeObserver.java +145 −34 Original line number Diff line number Diff line Loading @@ -32,12 +32,18 @@ import java.util.concurrent.CopyOnWriteArrayList; * for more information. */ public final class ViewTreeObserver { // Recursive listeners use CopyOnWriteArrayList private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners; private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners; private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners; private ArrayList<OnPreDrawListener> mOnPreDrawListeners; // Non-recursive listeners use CopyOnWriteArray // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners; private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners; private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners; // These listeners cannot be mutated during dispatch private ArrayList<OnDrawListener> mOnDrawListeners; private boolean mAlive = true; Loading Loading @@ -363,7 +369,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnGlobalLayoutListeners == null) { mOnGlobalLayoutListeners = new CopyOnWriteArrayList<OnGlobalLayoutListener>(); mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>(); } mOnGlobalLayoutListeners.add(listener); Loading Loading @@ -413,7 +419,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnPreDrawListeners == null) { mOnPreDrawListeners = new ArrayList<OnPreDrawListener>(); mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>(); } mOnPreDrawListeners.add(listener); Loading Loading @@ -485,7 +491,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnScrollChangedListeners == null) { mOnScrollChangedListeners = new CopyOnWriteArrayList<OnScrollChangedListener>(); mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>(); } mOnScrollChangedListeners.add(listener); Loading Loading @@ -558,7 +564,7 @@ public final class ViewTreeObserver { if (mOnComputeInternalInsetsListeners == null) { mOnComputeInternalInsetsListeners = new CopyOnWriteArrayList<OnComputeInternalInsetsListener>(); new CopyOnWriteArray<OnComputeInternalInsetsListener>(); } mOnComputeInternalInsetsListeners.add(listener); Loading Loading @@ -640,10 +646,16 @@ public final class ViewTreeObserver { // 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 CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; if (listeners != null && listeners.size() > 0) { for (OnGlobalLayoutListener listener : listeners) { listener.onGlobalLayout(); 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(); } } } Loading @@ -658,17 +670,17 @@ public final class ViewTreeObserver { */ @SuppressWarnings("unchecked") public final boolean dispatchOnPreDraw() { // NOTE: we *must* clone the listener list to perform the dispatching. // The clone 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 process it. boolean cancelDraw = false; if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) { final ArrayList<OnPreDrawListener> listeners = (ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone(); int numListeners = listeners.size(); for (int i = 0; i < numListeners; ++i) { cancelDraw |= !(listeners.get(i).onPreDraw()); final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners; if (listeners != null && listeners.size() > 0) { CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start(); try { int count = access.size(); for (int i = 0; i < count; i++) { cancelDraw |= !(access.get(i).onPreDraw()); } } finally { listeners.end(); } } return cancelDraw; Loading Loading @@ -710,10 +722,16 @@ public final class ViewTreeObserver { // 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 CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners; final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners; if (listeners != null && listeners.size() > 0) { for (OnScrollChangedListener listener : listeners) { listener.onScrollChanged(); CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start(); try { int count = access.size(); for (int i = 0; i < count; i++) { access.get(i).onScrollChanged(); } } finally { listeners.end(); } } } Loading @@ -722,7 +740,7 @@ public final class ViewTreeObserver { * Returns whether there are listeners for computing internal insets. */ final boolean hasComputeInternalInsetsListeners() { final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners; return (listeners != null && listeners.size() > 0); } Loading @@ -735,12 +753,105 @@ public final class ViewTreeObserver { // 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 CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners; if (listeners != null && listeners.size() > 0) { for (OnComputeInternalInsetsListener listener : listeners) { listener.onComputeInternalInsets(inoutInfo); CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start(); try { int count = access.size(); for (int i = 0; i < count; i++) { access.get(i).onComputeInternalInsets(inoutInfo); } } finally { listeners.end(); } } } /** * Copy on write array. This array is not thread safe, and only one loop can * iterate over this array at any given time. This class avoids allocations * until a concurrent modification happens. * * Usage: * * CopyOnWriteArray.Access<MyData> access = array.start(); * try { * for (int i = 0; i < access.size(); i++) { * MyData d = access.get(i); * } * } finally { * access.end(); * } */ static class CopyOnWriteArray<T> { private ArrayList<T> mData = new ArrayList<T>(); private ArrayList<T> mDataCopy; private final Access<T> mAccess = new Access<T>(); private boolean mStart; static class Access<T> { private ArrayList<T> mData; private int mSize; T get(int index) { return mData.get(index); } int size() { return mSize; } } CopyOnWriteArray() { } private ArrayList<T> getArray() { if (mStart) { if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData); return mDataCopy; } return mData; } Access<T> start() { if (mStart) throw new IllegalStateException("Iteration already started"); mStart = true; mDataCopy = null; mAccess.mData = mData; mAccess.mSize = mData.size(); return mAccess; } void end() { if (!mStart) throw new IllegalStateException("Iteration not started"); mStart = false; if (mDataCopy != null) { mData = mDataCopy; } mDataCopy = null; } int size() { return getArray().size(); } void add(T item) { getArray().add(item); } void addAll(CopyOnWriteArray<T> array) { getArray().addAll(array.mData); } void remove(T item) { getArray().remove(item); } void clear() { getArray().clear(); } } } Loading
core/java/android/view/ViewTreeObserver.java +145 −34 Original line number Diff line number Diff line Loading @@ -32,12 +32,18 @@ import java.util.concurrent.CopyOnWriteArrayList; * for more information. */ public final class ViewTreeObserver { // Recursive listeners use CopyOnWriteArrayList private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners; private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners; private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners; private ArrayList<OnPreDrawListener> mOnPreDrawListeners; // Non-recursive listeners use CopyOnWriteArray // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners; private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners; private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners; // These listeners cannot be mutated during dispatch private ArrayList<OnDrawListener> mOnDrawListeners; private boolean mAlive = true; Loading Loading @@ -363,7 +369,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnGlobalLayoutListeners == null) { mOnGlobalLayoutListeners = new CopyOnWriteArrayList<OnGlobalLayoutListener>(); mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>(); } mOnGlobalLayoutListeners.add(listener); Loading Loading @@ -413,7 +419,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnPreDrawListeners == null) { mOnPreDrawListeners = new ArrayList<OnPreDrawListener>(); mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>(); } mOnPreDrawListeners.add(listener); Loading Loading @@ -485,7 +491,7 @@ public final class ViewTreeObserver { checkIsAlive(); if (mOnScrollChangedListeners == null) { mOnScrollChangedListeners = new CopyOnWriteArrayList<OnScrollChangedListener>(); mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>(); } mOnScrollChangedListeners.add(listener); Loading Loading @@ -558,7 +564,7 @@ public final class ViewTreeObserver { if (mOnComputeInternalInsetsListeners == null) { mOnComputeInternalInsetsListeners = new CopyOnWriteArrayList<OnComputeInternalInsetsListener>(); new CopyOnWriteArray<OnComputeInternalInsetsListener>(); } mOnComputeInternalInsetsListeners.add(listener); Loading Loading @@ -640,10 +646,16 @@ public final class ViewTreeObserver { // 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 CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; if (listeners != null && listeners.size() > 0) { for (OnGlobalLayoutListener listener : listeners) { listener.onGlobalLayout(); 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(); } } } Loading @@ -658,17 +670,17 @@ public final class ViewTreeObserver { */ @SuppressWarnings("unchecked") public final boolean dispatchOnPreDraw() { // NOTE: we *must* clone the listener list to perform the dispatching. // The clone 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 process it. boolean cancelDraw = false; if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) { final ArrayList<OnPreDrawListener> listeners = (ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone(); int numListeners = listeners.size(); for (int i = 0; i < numListeners; ++i) { cancelDraw |= !(listeners.get(i).onPreDraw()); final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners; if (listeners != null && listeners.size() > 0) { CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start(); try { int count = access.size(); for (int i = 0; i < count; i++) { cancelDraw |= !(access.get(i).onPreDraw()); } } finally { listeners.end(); } } return cancelDraw; Loading Loading @@ -710,10 +722,16 @@ public final class ViewTreeObserver { // 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 CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners; final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners; if (listeners != null && listeners.size() > 0) { for (OnScrollChangedListener listener : listeners) { listener.onScrollChanged(); CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start(); try { int count = access.size(); for (int i = 0; i < count; i++) { access.get(i).onScrollChanged(); } } finally { listeners.end(); } } } Loading @@ -722,7 +740,7 @@ public final class ViewTreeObserver { * Returns whether there are listeners for computing internal insets. */ final boolean hasComputeInternalInsetsListeners() { final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners; return (listeners != null && listeners.size() > 0); } Loading @@ -735,12 +753,105 @@ public final class ViewTreeObserver { // 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 CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners; if (listeners != null && listeners.size() > 0) { for (OnComputeInternalInsetsListener listener : listeners) { listener.onComputeInternalInsets(inoutInfo); CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start(); try { int count = access.size(); for (int i = 0; i < count; i++) { access.get(i).onComputeInternalInsets(inoutInfo); } } finally { listeners.end(); } } } /** * Copy on write array. This array is not thread safe, and only one loop can * iterate over this array at any given time. This class avoids allocations * until a concurrent modification happens. * * Usage: * * CopyOnWriteArray.Access<MyData> access = array.start(); * try { * for (int i = 0; i < access.size(); i++) { * MyData d = access.get(i); * } * } finally { * access.end(); * } */ static class CopyOnWriteArray<T> { private ArrayList<T> mData = new ArrayList<T>(); private ArrayList<T> mDataCopy; private final Access<T> mAccess = new Access<T>(); private boolean mStart; static class Access<T> { private ArrayList<T> mData; private int mSize; T get(int index) { return mData.get(index); } int size() { return mSize; } } CopyOnWriteArray() { } private ArrayList<T> getArray() { if (mStart) { if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData); return mDataCopy; } return mData; } Access<T> start() { if (mStart) throw new IllegalStateException("Iteration already started"); mStart = true; mDataCopy = null; mAccess.mData = mData; mAccess.mSize = mData.size(); return mAccess; } void end() { if (!mStart) throw new IllegalStateException("Iteration not started"); mStart = false; if (mDataCopy != null) { mData = mDataCopy; } mDataCopy = null; } int size() { return getArray().size(); } void add(T item) { getArray().add(item); } void addAll(CopyOnWriteArray<T> array) { getArray().addAll(array.mData); } void remove(T item) { getArray().remove(item); } void clear() { getArray().clear(); } } }