Synchronize task threading (#933)
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieTask.java b/lottie/src/main/java/com/airbnb/lottie/LottieTask.java
index 866d0a5..fc77363 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieTask.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieTask.java
@@ -42,7 +42,7 @@
private final Handler handler = new Handler(Looper.getMainLooper());
private final FutureTask<LottieResult<T>> task;
- @Nullable private LottieResult<T> result = null;
+ @Nullable private volatile LottieResult<T> result = null;
@RestrictTo(RestrictTo.Scope.LIBRARY)
public LottieTask(Callable<LottieResult<T>> runnable) {
@@ -80,14 +80,12 @@
* Add a task listener. If the task has completed, the listener will be called synchronously.
* @return the task for call chaining.
*/
- public LottieTask<T> addListener(LottieListener<T> listener) {
+ public synchronized LottieTask<T> addListener(LottieListener<T> listener) {
if (result != null && result.getValue() != null) {
listener.onResult(result.getValue());
}
- synchronized (successListeners) {
- successListeners.add(listener);
- }
+ successListeners.add(listener);
startTaskObserverIfNeeded();
return this;
}
@@ -97,10 +95,8 @@
* a listener if neccesary.
* @return the task for call chaining.
*/
- public LottieTask<T> removeListener(LottieListener<T> listener) {
- synchronized (successListeners) {
- successListeners.remove(listener);
- }
+ public synchronized LottieTask<T> removeListener(LottieListener<T> listener) {
+ successListeners.remove(listener);
stopTaskObserverIfNeeded();
return this;
}
@@ -110,14 +106,12 @@
* occurs. If an exception has already occurred, the listener will be called immediately.
* @return the task for call chaining.
*/
- public LottieTask<T> addFailureListener(LottieListener<Throwable> listener) {
+ public synchronized LottieTask<T> addFailureListener(LottieListener<Throwable> listener) {
if (result != null && result.getException() != null) {
listener.onResult(result.getException());
}
- synchronized (failureListeners) {
- failureListeners.add(listener);
- }
+ failureListeners.add(listener);
startTaskObserverIfNeeded();
return this;
}
@@ -127,10 +121,8 @@
* a listener if neccesary.
* @return the task for call chaining.
*/
- public LottieTask<T> removeFailureListener(LottieListener<T> listener) {
- synchronized (failureListeners) {
- failureListeners.remove(listener);
- }
+ public synchronized LottieTask<T> removeFailureListener(LottieListener<T> listener) {
+ failureListeners.remove(listener);
stopTaskObserverIfNeeded();
return this;
}
@@ -154,7 +146,7 @@
}
private void notifySuccessListeners(T value) {
- // Allow listeners to remove themself in onResult.
+ // Allows listeners to remove themselves in onResult.
// Otherwise we risk ConcurrentModificationException.
List<LottieListener<T>> listenersCopy = new ArrayList<>(successListeners);
for (LottieListener<T> l : listenersCopy) {
@@ -163,7 +155,7 @@
}
private void notifyFailureListeners(Throwable e) {
- // Allow listeners to remove themself in onResult.
+ // Allows listeners to remove themselves in onResult.
// Otherwise we risk ConcurrentModificationException.
List<LottieListener<Throwable>> listenersCopy = new ArrayList<>(failureListeners);
if (listenersCopy.isEmpty()) {
@@ -180,14 +172,16 @@
* We monitor the task with an observer thread to determine when it is done and should notify
* the appropriate listeners.
*/
- private void startTaskObserverIfNeeded() {
+ private synchronized void startTaskObserverIfNeeded() {
if (taskObserverAlive() || result != null) {
return;
}
taskObserver = new Thread("LottieTaskObserver") {
+ private boolean taskComplete = false;
+
@Override public void run() {
while (true) {
- if (isInterrupted()) {
+ if (isInterrupted() || taskComplete) {
return;
}
if (task.isDone()) {
@@ -196,6 +190,7 @@
} catch (InterruptedException | ExecutionException e) {
setResult(new LottieResult<T>(e));
}
+ taskComplete = true;
stopTaskObserverIfNeeded();
}
}
@@ -208,7 +203,7 @@
/**
* We can stop observing the task if there are no more listeners or if the task is complete.
*/
- private void stopTaskObserverIfNeeded() {
+ private synchronized void stopTaskObserverIfNeeded() {
if (!taskObserverAlive()) {
return;
}