Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent NPE in ImageLayer.getBounds() #2578

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

allenchen1154
Copy link
Contributor

The recent improvements to drop shadows added a dereference of a nullable result from the getBitmap() call.

The recent [improvements to drop shadows](https://github.com/airbnb/lottie-android/pull/2548/files#diff-31e777f53a917d69dcf1b234ae6c77db843316c34911e200d0a9a160c058b621R110) added a dereference of a nullable result from the `getBitmap()` call.
@allenchen1154
Copy link
Contributor Author

Full stacktrace for reference:

java.lang.NullPointerException: Attempt to invoke virtual method 'int android.graphics.Bitmap.getWidth()' on a null object reference
        at com.airbnb.lottie.model.layer.ImageLayer.getBounds(ImageLayer:110)
        at com.airbnb.lottie.model.layer.BaseLayer.draw(BaseLayer:281)
        at com.airbnb.lottie.model.layer.CompositionLayer.drawLayer(CompositionLayer:161)
        at com.airbnb.lottie.model.layer.BaseLayer.draw(BaseLayer:270)
        at com.airbnb.lottie.model.layer.CompositionLayer.drawLayer(CompositionLayer:161)
        at com.airbnb.lottie.model.layer.BaseLayer.draw(BaseLayer:270)
        at com.airbnb.lottie.LottieDrawable.draw(LottieDrawable:826)
        at com.airbnb.lottie.LottieDrawable.draw(LottieDrawable:804)
        at com.airbnb.lottie.compose.LottieAnimationKt$LottieAnimation$2.invoke(LottieAnimationKt:145)
        at com.airbnb.lottie.compose.LottieAnimationKt$LottieAnimation$2.invoke(LottieAnimationKt:107)
        at androidx.compose.ui.draw.DrawBackgroundModifier.draw(DrawBackgroundModifier:116)
        at androidx.compose.ui.node.LayoutNodeDrawScope.drawDirect-x_KDEd0$ui_release(LayoutNodeDrawScope:105)
        at androidx.compose.ui.node.LayoutNodeDrawScope.draw-x_KDEd0$ui_release(LayoutNodeDrawScope:86)
        at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator:364)
        at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator:353)
        at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator:176)
        at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator:361)
        at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator:353)
        at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator:176)
        at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator:361)
        at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator:353)
        at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode:926)
        at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator:174)
        at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator:361)
        at androidx.compose.ui.node.NodeCoordinator.access$drawContainedDrawModifiers(NodeCoordinator:54)
        at androidx.compose.ui.node.NodeCoordinator$drawBlock$1$1.invoke(NodeCoordinator:383)
        at androidx.compose.foundation.FocusablePinnableContainerNode$retrievePinnableContainer$1.invoke(FocusablePinnableContainerNode:75)
        at androidx.compose.ui.node.NodeCoordinator$drawBlock$1$1.invoke(NodeCoordinator:382)
        at androidx.compose.foundation.FocusablePinnableContainerNode$retrievePinnableContainer$1.invoke(FocusablePinnableContainerNode:20)
        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot:2303)
        at com.airbnb.android.utils.ParcelableUtils.observe(ParcelableUtils:59)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver:500)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver:51)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver:256)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver:140)
        at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver:133)
        at androidx.compose.ui.node.NodeCoordinator$drawBlock$1.invoke(NodeCoordinator:382)
        at androidx.compose.ui.node.NodeCoordinator$drawBlock$1.invoke(NodeCoordinator:380)
        at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier$layerBlock$1.invoke(SimpleGraphicsLayerModifier:91)
        at androidx.compose.ui.platform.RenderNodeApi29.record(RenderNodeApi29:209)
        at androidx.compose.ui.platform.RenderNodeLayer.updateDisplayList(RenderNodeLayer:335)
        at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView:1236)
        at android.view.View.draw(View.java:24630)
        at android.view.View.updateDisplayListIfDirty(View.java:23493)
        at android.view.View.draw(View.java:24357)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4576)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4337)
        at android.view.View.updateDisplayListIfDirty(View.java:23484)
        at android.view.View.draw(View.java:24357)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4576)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4337)
        at android.view.View.updateDisplayListIfDirty(View.java:23484)
        at android.view.View.draw(View.java:24357)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4576)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4337)
        at android.view.View.updateDisplayListIfDirty(View.java:23484)
        at android.view.View.draw(View.java:24357)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4576)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4337)
        at android.view.View.draw(View.java:24630)
        at com.android.internal.policy.DecorView.draw(DecorView.java:827)
        at android.view.View.updateDisplayListIfDirty(View.java:23493)
        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:694)
        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:700)
        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:798)
        at android.view.ViewRootImpl.draw(ViewRootImpl.java:5313)
        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4975)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4093)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2718)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9937)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1406)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1415)
        at android.view.Choreographer.doCallbacks(Choreographer.java:1015)
        at android.view.Choreographer.doFrame(Choreographer.java:945)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1389)
        at android.os.Handler.handleCallback(Handler.java:959)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loopOnce(Looper.java:232)
        at android.os.Looper.loop(Looper.java:317)
        at android.app.ActivityThread.main(ActivityThread.java:8592)
        at java.lang.reflect.Method.invoke(Method.java:-2)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)

Copy link

Snapshot Tests
API 23: Report Diff
API 31: Report Diff

@@ -107,7 +107,10 @@ public class ImageLayer extends BaseLayer {
if (lottieDrawable.getMaintainOriginalImageBounds()) {
outBounds.set(0, 0, lottieImageAsset.getWidth() * scale, lottieImageAsset.getHeight() * scale);
} else {
outBounds.set(0, 0, getBitmap().getWidth() * scale, getBitmap().getHeight() * scale);
Bitmap bitmap = getBitmap();
if (bitmap != null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if this is null? Will outBounds potentially have a stale value from another frame?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gpeal I don't have the full context here so maybe @geomaster can chime in on what scenarios would cause the bitmap to be null and whether we need alternate behavior around updating outBounds.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I'm not sure why the bitmap would be null - presumably, it has not loaded yet, or it didn't load correctly?

If the bitmap is null, we would be rendering nothing, so I think arguably the correct thing to do is simply to set to an empty rectangle, with outBounds.set(0, 0, 0, 0).

Thoughts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants