Skip to content

Commit

Permalink
Do not hold strong reference to LottieAnimationView in success/failur…
Browse files Browse the repository at this point in the history
…e listeners (#2293)

Fixes #2292

I followed `LeakCanary` hints, identified 2 listeners that were kept in the static map and fixed them by holding a weak reference to a target object (using private static classes). 

As far as I understood the flow, there should be no behavior change, other than not having a memory leak. The view can be immediately garbage collected, without altering existing behavior (the resource will continue being fetched in background).
  • Loading branch information
mateuszkwiecinski committed May 12, 2023
1 parent a0bf926 commit d57e718
Showing 1 changed file with 40 additions and 8 deletions.
48 changes: 40 additions & 8 deletions lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -74,18 +75,49 @@
throw new IllegalStateException("Unable to parse composition", throwable);
};

private final LottieListener<LottieComposition> loadedListener = this::setComposition;
private final LottieListener<LottieComposition> loadedListener = new WeakSuccessListener(this);

private final LottieListener<Throwable> wrappedFailureListener = new LottieListener<Throwable>() {
@Override
public void onResult(Throwable result) {
if (fallbackResource != 0) {
setImageResource(fallbackResource);
private static class WeakSuccessListener implements LottieListener<LottieComposition> {

private final WeakReference<LottieAnimationView> targetReference;

public WeakSuccessListener(LottieAnimationView target) {
this.targetReference = new WeakReference<>(target);
}

@Override public void onResult(LottieComposition result) {
LottieAnimationView targetView = targetReference.get();
if (targetView == null) {
return;
}
LottieListener<Throwable> l = failureListener == null ? DEFAULT_FAILURE_LISTENER : failureListener;
targetView.setComposition(result);
}
}

private final LottieListener<Throwable> wrappedFailureListener = new WeakFailureListener(this);

private static class WeakFailureListener implements LottieListener<Throwable> {

private final WeakReference<LottieAnimationView> targetReference;

public WeakFailureListener(LottieAnimationView target) {
this.targetReference = new WeakReference<>(target);
}

@Override public void onResult(Throwable result) {
LottieAnimationView targetView = targetReference.get();
if (targetView == null) {
return;
}

if (targetView.fallbackResource != 0) {
targetView.setImageResource(targetView.fallbackResource);
}
LottieListener<Throwable> l = targetView.failureListener == null ? DEFAULT_FAILURE_LISTENER : targetView.failureListener;
l.onResult(result);
}
};
}

@Nullable private LottieListener<Throwable> failureListener;
@DrawableRes private int fallbackResource = 0;

Expand Down

0 comments on commit d57e718

Please sign in to comment.