[GH-ISSUE #474] Android: notifications with large images attached crash the app #363

Closed
opened 2026-05-07 00:23:30 +02:00 by BreizhHardware · 5 comments

Originally created by @daedric7 on GitHub (Nov 8, 2022).
Original GitHub issue: https://github.com/binwiederhier/ntfy/issues/474

Hello

I've noticed this for a while but has it's only trigger by very large images, it's difficult to find a case.

A notification sent via curl with a very large image attached to a topic makes the app crash when opening that topic.

All other topics keep working, the app keeps receiving more notifications on that topic although unable to display them.

The last image to cause this was this: (WARNING: NSFW!)

https://i.imgur.com/pKiQk6l.jpg , 4.4mb 4480x6720

Android creates a log report and sends it somewhere, but i cannot intercept it.

The only error i found was:

java.lang.RuntimeException: Canvas: trying to draw too large(120422400bytes) bitmap.

Originally created by @daedric7 on GitHub (Nov 8, 2022). Original GitHub issue: https://github.com/binwiederhier/ntfy/issues/474 Hello I've noticed this for a while but has it's only trigger by very large images, it's difficult to find a case. A notification sent via curl with a very large image attached to a topic makes the app crash when opening that topic. All other topics keep working, the app keeps receiving more notifications on that topic although unable to display them. The last image to cause this was this: (WARNING: NSFW!) https://i.imgur.com/pKiQk6l.jpg , 4.4mb 4480x6720 Android creates a log report and sends it somewhere, but i cannot intercept it. The only error i found was: `java.lang.RuntimeException: Canvas: trying to draw too large(120422400bytes) bitmap.`
BreizhHardware 2026-05-07 00:23:30 +02:00
Author
Owner

@binwiederhier commented on GitHub (Nov 8, 2022):

I have experienced crashes with large photos as well, so I can confirm this happens. I think this should be one of the next Android tickets to be worked on. The app should never crash.

(Side note: You could have linked any other large photo ... Like, one from NASA or Wikipedia...)

<!-- gh-comment-id:1307721278 --> @binwiederhier commented on GitHub (Nov 8, 2022): I have experienced crashes with large photos as well, so I can confirm this happens. I think this should be one of the next Android tickets to be worked on. The app should never crash. (Side note: You could have linked any other large photo ... Like, one from NASA or Wikipedia...)
Author
Owner

@binwiederhier commented on GitHub (Nov 19, 2022):

This is a little trickier than expected, because I cannot catch a RuntimeException, and whether the exception is raised depends on your system and your available RAM. Figuring out whether an image is too large is non-trivial, because a JPG of 4480x6720 (as you posted) expends to 120 MB in memory. I could limit previews to 1 MB JPG files, or to certain dimensions, but it's not a sure thing.

The only way to solve this would be to bring in a library that intelligently does the image loading in a memory efficient way. And I' not sure I want to bring in a library just for that.

(I am working on this though, and will hopefully have a solution soon)

<!-- gh-comment-id:1320892816 --> @binwiederhier commented on GitHub (Nov 19, 2022): This is a little trickier than expected, because I cannot catch a `RuntimeException`, and whether the exception is raised depends on your system and your available RAM. Figuring out whether an image is too large is non-trivial, because a JPG of 4480x6720 (as you posted) expends to 120 MB in memory. I could limit previews to 1 MB JPG files, or to certain dimensions, but it's not a _sure thing_. The only way to solve this would be to bring in a library that intelligently does the image loading in a memory efficient way. And I' not sure I want to bring in a library just for that. (I am working on this though, and will hopefully have a solution soon)
Author
Owner

@daedric7 commented on GitHub (Nov 19, 2022):

Perhaps the server, upon receiving a image could create a thumbnail. The whole file and the thumbnail could be passed to the Android app, but only the thumb would be previewed.

In anycase, i find this issue peculiar. The tested smartphone is not weak on memory, 8GB.

<!-- gh-comment-id:1320994406 --> @daedric7 commented on GitHub (Nov 19, 2022): Perhaps the server, upon receiving a image could create a thumbnail. The whole file and the thumbnail could be passed to the Android app, but only the thumb would be previewed. In anycase, i find this issue peculiar. The tested smartphone is not weak on memory, 8GB.
Author
Owner

@binwiederhier commented on GitHub (Nov 27, 2022):

This is the corresponding stack trace I got from the Google Play console for this issue; and from reproducing it locally:

Exception java.lang.RuntimeException: Canvas: trying to draw too large(120422400bytes) bitmap.
  at android.graphics.RecordingCanvas.throwIfCannotDraw (RecordingCanvas.java:266)
  at android.graphics.BaseRecordingCanvas.drawBitmap (BaseRecordingCanvas.java:94)
  at android.graphics.drawable.BitmapDrawable.draw (BitmapDrawable.java:549)
  at android.widget.ImageView.onDraw (ImageView.java:1446)
  at com.google.android.material.imageview.ShapeableImageView.onDraw (ShapeableImageView.java:198)
  at android.view.View.draw (View.java:23195)
  at android.view.View.updateDisplayListIfDirty (View.java:22062)
  at android.view.View.draw (View.java:22926)
  at android.view.ViewGroup.drawChild (ViewGroup.java:4529)
  at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290)
  at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw (ConstraintLayout.java:1994)
  at android.view.View.draw (View.java:23198)
  at android.view.View.updateDisplayListIfDirty (View.java:22062)
  at android.view.View.draw (View.java:22926)
  at android.view.ViewGroup.drawChild (ViewGroup.java:4529)
  at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290)
  at android.view.View.draw (View.java:23198)
  at android.view.View.updateDisplayListIfDirty (View.java:22062)
  at android.view.View.draw (View.java:22926)
  at android.view.ViewGroup.drawChild (ViewGroup.java:4529)
  at androidx.recyclerview.widget.RecyclerView.drawChild (RecyclerView.java:5370)
  at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290)
  at android.view.View.draw (View.java:23198)
  at androidx.recyclerview.widget.RecyclerView.draw (RecyclerView.java:4769)
  at android.view.View.updateDisplayListIfDirty (View.java:22062)
  at android.view.View.draw (View.java:22926)
  at android.view.ViewGroup.drawChild (ViewGroup.java:4529)
  at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290)
  at android.view.View.draw (View.java:23198)
  at android.view.View.updateDisplayListIfDirty (View.java:22062)
  at android.view.View.draw (View.java:22926)
  at android.view.ViewGroup.drawChild (ViewGroup.java:4529)
  at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290)
  at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw (ConstraintLayout.java:1994)
  at android.view.View.updateDisplayListIfDirty (View.java:22053)
  at android.view.View.draw (View.java:22926)
  at android.view.ViewGroup.drawChild (ViewGroup.java:4529)
  at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290)
  at android.view.View.updateDisplayListIfDirty (View.java:22053)
  at android.view.View.draw (View.java:22926)
  at android.view.ViewGroup.drawChild (ViewGroup.java:4529)
  at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290)
  at android.view.View.updateDisplayListIfDirty (View.java:22053)
  at android.view.View.draw (View.java:22926)
  at android.view.ViewGroup.drawChild (ViewGroup.java:4529)
  at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290)
  at android.view.View.updateDisplayListIfDirty (View.java:22053)
  at android.view.View.draw (View.java:22926)
  at android.view.ViewGroup.drawChild (ViewGroup.java:4529)
  at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290)
  at android.view.View.updateDisplayListIfDirty (View.java:22053)
  at android.view.View.draw (View.java:22926)
  at android.view.ViewGroup.drawChild (ViewGroup.java:4529)
  at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290)
  at android.view.View.draw (View.java:23198)
  at com.android.internal.policy.DecorView.draw (DecorView.java:819)
  at android.view.View.updateDisplayListIfDirty (View.java:22062)
  at android.view.ThreadedRenderer.updateViewTreeDisplayList (ThreadedRenderer.java:682)
  at android.view.ThreadedRenderer.updateRootDisplayList (ThreadedRenderer.java:688)
  at android.view.ThreadedRenderer.draw (ThreadedRenderer.java:786)
  at android.view.ViewRootImpl.draw (ViewRootImpl.java:4579)
  at android.view.ViewRootImpl.performDraw (ViewRootImpl.java:4290)
  at android.view.ViewRootImpl.performTraversals (ViewRootImpl.java:3517)
  at android.view.ViewRootImpl.doTraversal (ViewRootImpl.java:2286)
  at android.view.ViewRootImpl$TraversalRunnable.run (ViewRootImpl.java:8948)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1231)
  at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1239)
  at android.view.Choreographer.doCallbacks (Choreographer.java:899)
  at android.view.Choreographer.doFrame (Choreographer.java:832)
  at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:1214)
  at android.os.Handler.handleCallback (Handler.java:942)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loopOnce (Looper.java:201)
  at android.os.Looper.loop (Looper.java:288)
  at android.app.ActivityThread.main (ActivityThread.java:7893)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:936)
<!-- gh-comment-id:1328301896 --> @binwiederhier commented on GitHub (Nov 27, 2022): This is the corresponding stack trace I got from the Google Play console for this issue; and from reproducing it locally: ``` Exception java.lang.RuntimeException: Canvas: trying to draw too large(120422400bytes) bitmap. at android.graphics.RecordingCanvas.throwIfCannotDraw (RecordingCanvas.java:266) at android.graphics.BaseRecordingCanvas.drawBitmap (BaseRecordingCanvas.java:94) at android.graphics.drawable.BitmapDrawable.draw (BitmapDrawable.java:549) at android.widget.ImageView.onDraw (ImageView.java:1446) at com.google.android.material.imageview.ShapeableImageView.onDraw (ShapeableImageView.java:198) at android.view.View.draw (View.java:23195) at android.view.View.updateDisplayListIfDirty (View.java:22062) at android.view.View.draw (View.java:22926) at android.view.ViewGroup.drawChild (ViewGroup.java:4529) at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290) at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw (ConstraintLayout.java:1994) at android.view.View.draw (View.java:23198) at android.view.View.updateDisplayListIfDirty (View.java:22062) at android.view.View.draw (View.java:22926) at android.view.ViewGroup.drawChild (ViewGroup.java:4529) at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290) at android.view.View.draw (View.java:23198) at android.view.View.updateDisplayListIfDirty (View.java:22062) at android.view.View.draw (View.java:22926) at android.view.ViewGroup.drawChild (ViewGroup.java:4529) at androidx.recyclerview.widget.RecyclerView.drawChild (RecyclerView.java:5370) at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290) at android.view.View.draw (View.java:23198) at androidx.recyclerview.widget.RecyclerView.draw (RecyclerView.java:4769) at android.view.View.updateDisplayListIfDirty (View.java:22062) at android.view.View.draw (View.java:22926) at android.view.ViewGroup.drawChild (ViewGroup.java:4529) at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290) at android.view.View.draw (View.java:23198) at android.view.View.updateDisplayListIfDirty (View.java:22062) at android.view.View.draw (View.java:22926) at android.view.ViewGroup.drawChild (ViewGroup.java:4529) at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290) at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw (ConstraintLayout.java:1994) at android.view.View.updateDisplayListIfDirty (View.java:22053) at android.view.View.draw (View.java:22926) at android.view.ViewGroup.drawChild (ViewGroup.java:4529) at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290) at android.view.View.updateDisplayListIfDirty (View.java:22053) at android.view.View.draw (View.java:22926) at android.view.ViewGroup.drawChild (ViewGroup.java:4529) at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290) at android.view.View.updateDisplayListIfDirty (View.java:22053) at android.view.View.draw (View.java:22926) at android.view.ViewGroup.drawChild (ViewGroup.java:4529) at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290) at android.view.View.updateDisplayListIfDirty (View.java:22053) at android.view.View.draw (View.java:22926) at android.view.ViewGroup.drawChild (ViewGroup.java:4529) at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290) at android.view.View.updateDisplayListIfDirty (View.java:22053) at android.view.View.draw (View.java:22926) at android.view.ViewGroup.drawChild (ViewGroup.java:4529) at android.view.ViewGroup.dispatchDraw (ViewGroup.java:4290) at android.view.View.draw (View.java:23198) at com.android.internal.policy.DecorView.draw (DecorView.java:819) at android.view.View.updateDisplayListIfDirty (View.java:22062) at android.view.ThreadedRenderer.updateViewTreeDisplayList (ThreadedRenderer.java:682) at android.view.ThreadedRenderer.updateRootDisplayList (ThreadedRenderer.java:688) at android.view.ThreadedRenderer.draw (ThreadedRenderer.java:786) at android.view.ViewRootImpl.draw (ViewRootImpl.java:4579) at android.view.ViewRootImpl.performDraw (ViewRootImpl.java:4290) at android.view.ViewRootImpl.performTraversals (ViewRootImpl.java:3517) at android.view.ViewRootImpl.doTraversal (ViewRootImpl.java:2286) at android.view.ViewRootImpl$TraversalRunnable.run (ViewRootImpl.java:8948) at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1231) at android.view.Choreographer$CallbackRecord.run (Choreographer.java:1239) at android.view.Choreographer.doCallbacks (Choreographer.java:899) at android.view.Choreographer.doFrame (Choreographer.java:832) at android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:1214) at android.os.Handler.handleCallback (Handler.java:942) at android.os.Handler.dispatchMessage (Handler.java:99) at android.os.Looper.loopOnce (Looper.java:201) at android.os.Looper.loop (Looper.java:288) at android.app.ActivityThread.main (ActivityThread.java:7893) at java.lang.reflect.Method.invoke (Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:936) ```
Author
Owner

@binwiederhier commented on GitHub (Nov 27, 2022):

I managed to monkey patch this using this fix: github.com/binwiederhier/ntfy-android@d09fdb2ff2 - The workaround is from here: https://stackoverflow.com/a/53334563/1440785

It works by checking if the loaded image is > 100 MB in memory and exits out gracefully if it does. The image is not rendered in that case. I could have gone for a "resize and display" option, but I think this is sufficient for now.

Will be in the next Android release.

<!-- gh-comment-id:1328302681 --> @binwiederhier commented on GitHub (Nov 27, 2022): I managed to monkey patch this using this fix: https://github.com/binwiederhier/ntfy-android/commit/d09fdb2ff259f9e4d8cf83b515d37f4d3062d6b3 - The workaround is from here: https://stackoverflow.com/a/53334563/1440785 It works by checking if the loaded image is > 100 MB in memory and exits out gracefully if it does. The image is not rendered in that case. I could have gone for a "resize and display" option, but I think this is sufficient for now. Will be in the next Android release.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/ntfy#363
No description provided.