Description
PlaybackNotification.loadArtwork (L509-539) downloads and decodes remote artwork at full resolution:
val connection = URL(url).openConnection()
connection.connect()
val inputStream = connection.getInputStream()
val bitmap = BitmapFactory.decodeStream(inputStream)
- Unbounded decode — no
BitmapFactory.Options, so a 4000×4000 artwork allocates ~64 MB in ARGB_8888 for something rendered at notification-icon size. BitmapFactory.decodeFile in the local branch has the same issue.
- No cache — every metadata update re-downloads and re-decodes the same URL.
- No timeouts — default (infinite) connect/read on a bare
Thread that interrupt() can't unblock from a socket read.
- The result is then passed to
mediaSession.setMetadata(...putBitmap(METADATA_KEY_ART, bitmap)) (L288), crossing a Binder transaction at full size.
Google Play Console flags this under App quality insights → "bitmap image optimization":
Decoded in com.swmansion.audioapi.system.notification.PlaybackNotification.loadArtwork
Downloaded in com.swmansion.audioapi.system.notification.PlaybackNotification.loadArtwork
Suggested fix: two-pass decode (inJustDecodeBounds → inSampleSize against notification_large_icon_width/height or a ~512 px cap), plus connect/read timeouts and a small LRU on the URL. Alternatively delegate to Fresco, already on the classpath in every RN app, via ImageRequestBuilder + ResizeOptions — that gets downsampling, caching and cancellation for free.
Steps to reproduce
AudioManager.setNowPlaying({ ..., artwork: '<url to a 4000×4000 JPEG>' }).
- Android Studio Profiler shows the bitmap allocated at source resolution, not icon size. On low-RAM devices this surfaces as
OutOfMemoryError in BitmapFactory.decodeStream.
Snack or a link to a repository
None — code-level report, verified against main; source linked above.
React Native Audio API version
0.13.2 (unchanged on main and in 0.13.3)
React Native version
0.86.3
Platforms
Android
Architecture
New architecture
Build type
Release mode
Description
PlaybackNotification.loadArtwork(L509-539) downloads and decodes remote artwork at full resolution:BitmapFactory.Options, so a 4000×4000 artwork allocates ~64 MB inARGB_8888for something rendered at notification-icon size.BitmapFactory.decodeFilein the local branch has the same issue.Threadthatinterrupt()can't unblock from a socket read.mediaSession.setMetadata(...putBitmap(METADATA_KEY_ART, bitmap))(L288), crossing a Binder transaction at full size.Google Play Console flags this under App quality insights → "bitmap image optimization":
Suggested fix: two-pass decode (
inJustDecodeBounds→inSampleSizeagainstnotification_large_icon_width/heightor a ~512 px cap), plus connect/read timeouts and a small LRU on the URL. Alternatively delegate to Fresco, already on the classpath in every RN app, viaImageRequestBuilder+ResizeOptions— that gets downsampling, caching and cancellation for free.Steps to reproduce
AudioManager.setNowPlaying({ ..., artwork: '<url to a 4000×4000 JPEG>' }).OutOfMemoryErrorinBitmapFactory.decodeStream.Snack or a link to a repository
None — code-level report, verified against
main; source linked above.React Native Audio API version
0.13.2 (unchanged on
mainand in 0.13.3)React Native version
0.86.3
Platforms
Android
Architecture
New architecture
Build type
Release mode