Merge "Avoid Concurrent Exception in BaseDataProducer." into tm-qpr-dev am: 38a3a66278
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19593556 Change-Id: If13688d26785caf9f4b585e5d489338ff11d13a2 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package androidx.window.util;
|
package androidx.window.util;
|
||||||
|
|
||||||
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@@ -25,26 +26,46 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class that provides the implementation for the callback mechanism of the
|
* Base class that provides the implementation for the callback mechanism of the
|
||||||
* {@link DataProducer} API.
|
* {@link DataProducer} API. This class is thread safe for adding, removing, and notifying
|
||||||
|
* consumers.
|
||||||
*
|
*
|
||||||
* @param <T> The type of data this producer returns through {@link DataProducer#getData}.
|
* @param <T> The type of data this producer returns through {@link DataProducer#getData}.
|
||||||
*/
|
*/
|
||||||
public abstract class BaseDataProducer<T> implements DataProducer<T> {
|
public abstract class BaseDataProducer<T> implements DataProducer<T> {
|
||||||
|
|
||||||
|
private final Object mLock = new Object();
|
||||||
|
@GuardedBy("mLock")
|
||||||
private final Set<Consumer<T>> mCallbacks = new LinkedHashSet<>();
|
private final Set<Consumer<T>> mCallbacks = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a callback to the set of callbacks listening for data. Data is delivered through
|
||||||
|
* {@link BaseDataProducer#notifyDataChanged(Object)}. This method is thread safe. Callers
|
||||||
|
* should ensure that callbacks are thread safe.
|
||||||
|
* @param callback that will receive data from the producer.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final void addDataChangedCallback(@NonNull Consumer<T> callback) {
|
public final void addDataChangedCallback(@NonNull Consumer<T> callback) {
|
||||||
|
synchronized (mLock) {
|
||||||
mCallbacks.add(callback);
|
mCallbacks.add(callback);
|
||||||
Optional<T> currentData = getCurrentData();
|
Optional<T> currentData = getCurrentData();
|
||||||
currentData.ifPresent(callback);
|
currentData.ifPresent(callback);
|
||||||
onListenersChanged(mCallbacks);
|
onListenersChanged(mCallbacks);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a callback to the set of callbacks listening for data. This method is thread safe
|
||||||
|
* for adding.
|
||||||
|
* @param callback that was registered in
|
||||||
|
* {@link BaseDataProducer#addDataChangedCallback(Consumer)}.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final void removeDataChangedCallback(@NonNull Consumer<T> callback) {
|
public final void removeDataChangedCallback(@NonNull Consumer<T> callback) {
|
||||||
|
synchronized (mLock) {
|
||||||
mCallbacks.remove(callback);
|
mCallbacks.remove(callback);
|
||||||
onListenersChanged(mCallbacks);
|
onListenersChanged(mCallbacks);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void onListenersChanged(Set<Consumer<T>> callbacks) {}
|
protected void onListenersChanged(Set<Consumer<T>> callbacks) {}
|
||||||
|
|
||||||
@@ -56,11 +77,14 @@ public abstract class BaseDataProducer<T> implements DataProducer<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to notify all registered consumers that the data provided
|
* Called to notify all registered consumers that the data provided
|
||||||
* by {@link DataProducer#getData} has changed.
|
* by {@link DataProducer#getData} has changed. Calls to this are thread save but callbacks need
|
||||||
|
* to ensure thread safety.
|
||||||
*/
|
*/
|
||||||
protected void notifyDataChanged(T value) {
|
protected void notifyDataChanged(T value) {
|
||||||
|
synchronized (mLock) {
|
||||||
for (Consumer<T> callback : mCallbacks) {
|
for (Consumer<T> callback : mCallbacks) {
|
||||||
callback.accept(value);
|
callback.accept(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user