lottie-android
lottie-android copied to clipboard
Animation using ImageAssetDelegate crashes when recreated
Describe the bug We are currently using an animation with placeholders to show some images dynamically.
Images are inside our res folder. The images we load are different depending on some business rules, so we rely on a custom ImageAssetDelegate
instead of using image asset folder.
This works great, but we've noticed it crashes in some scenarios, mainly when the fragment/activity is recreated. For example, in our case it crashes when system theme changes between light and dark mode. That recreates the fragment and the animation produces a crash.
This is the stacktrace we see:
java.lang.IllegalStateException: You must set an images folder before loading an image. Set it with LottieComposition#setImagesFolder or LottieDrawable#setImagesFolder
at com.airbnb.lottie.manager.ImageAssetManager.bitmapForId(ImageAssetManager.java:109)
at com.airbnb.lottie.LottieDrawable.getBitmapForId(LottieDrawable.java:1203)
at com.airbnb.lottie.model.layer.ImageLayer.getBitmap(ImageLayer.java:79)
at com.airbnb.lottie.model.layer.ImageLayer.drawLayer(ImageLayer.java:38)
at com.airbnb.lottie.model.layer.BaseLayer.draw(BaseLayer.java:250)
at com.airbnb.lottie.model.layer.CompositionLayer.drawLayer(CompositionLayer.java:128)
at com.airbnb.lottie.model.layer.BaseLayer.draw(BaseLayer.java:250)
at com.airbnb.lottie.LottieDrawable.drawDirectlyToCanvas(LottieDrawable.java:1354)
at com.airbnb.lottie.LottieDrawable.draw(LottieDrawable.java:517)
at android.widget.ImageView.onDraw(ImageView.java:1451)
at android.view.View.draw(View.java:22803)
at android.view.View.updateDisplayListIfDirty(View.java:21663)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4361)
at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw(ConstraintLayout.java:1994)
at android.view.View.draw(View.java:22806)
at android.view.View.updateDisplayListIfDirty(View.java:21663)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4361)
at android.view.View.draw(View.java:22806)
at android.view.View.updateDisplayListIfDirty(View.java:21663)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4361)
at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw(ConstraintLayout.java:1994)
at android.view.View.draw(View.java:22806)
at android.view.View.updateDisplayListIfDirty(View.java:21663)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4361)
at android.view.View.draw(View.java:22806)
at com.google.android.material.appbar.AppBarLayout.draw(AppBarLayout.java:417)
at android.view.View.updateDisplayListIfDirty(View.java:21663)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
at androidx.coordinatorlayout.widget.CoordinatorLayout.drawChild(CoordinatorLayout.java:1277)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4361)
at android.view.View.updateDisplayListIfDirty(View.java:21654)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4361)
at android.view.View.updateDisplayListIfDirty(View.java:21654)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4361)
at android.view.View.updateDisplayListIfDirty(View.java:21654)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4361)
at android.view.View.updateDisplayListIfDirty(View.java:21654)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4361)
at android.view.View.updateDisplayListIfDirty(View.java:21654)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4361)
at android.view.View.updateDisplayListIfDirty(View.java:21654)
at android.view.View.draw(View.java:22523)
at android.view.ViewGroup.drawChild(ViewGroup.java:4608)
My assumption is that assetDelegate is not restored and that fallbacks into trying to obtain the images from the asset folder.
Steps To Reproduce
If necessary I can provide a sandbox apk to reproduce the crash (or use the issue-repro module). Just wanted to check first if this is a known issue or if we are missing something.
Can you attach a project that reproduces this?
There you go: https://github.com/cmonfortep/LottieTest
How to reproduce:
- Open the app
- click on Play animation
- change system theme
- app crashes
Notice something important: We set the assetDelegate when we know the animation will appear. I've tried to simulate a similar scenario by setting the assetDelegate when you click the "Play animation" button. (see https://github.com/cmonfortep/LottieTest/blob/master/app/src/main/java/com/example/lottietest/TestActivity.kt#L24)
Same here. Please update
Lottie saves and restores its instance state. In this case, when the configuration change started, the animation was playing. After night mode changed, LottieAnimationView restored its state which was playing. However, your business logic had not yet set an image delegate, thus causing it to crash.
You can disable the saved instance state behavior with android:saveEnabled="false"
on your layout. I just tested it in your sample app and it prevented the crash from happening.