Skip to content

Commit

Permalink
Swallow exceptions on draw, forward to onError
Browse files Browse the repository at this point in the history
Summary:
Graceful degradation is better than outright crashing. When rendering images that exceed Android's memory limits, React Native applications will fatally crash. This change intervenes by swallowing the exception that Android raises and forwards it to the `onError` handler. As a result, no image will be rendered instead of fatally crashing.

Fresco already intervenes by default. It will raise a `PoolSizeViolationException` if the bitmap size exceeds memory limits set by the OS and applies a 2048 pixel maximum dimension to JPEG images, but it's still possible for these configuration limits to be removed and for images to fall just short of Fresco's memory limit. The exception is raised when we attempt to draw the bitmap, not when the bitmap is allocated in memory, so we must handle the exception here and not in Fresco.

## Changelog

[Android][Fixed] - Apps will no longer fatally crash when trying to draw large images

Differential Revision: D64144596
  • Loading branch information
Abbondanzo authored and facebook-github-bot committed Oct 10, 2024
1 parent e39c917 commit 9019e22
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,16 @@ public class ReactImageView(

public override fun onDraw(canvas: Canvas) {
BackgroundStyleApplicator.clipToPaddingBox(this, canvas)
super.onDraw(canvas)
try {
super.onDraw(canvas)
} catch (e: Exception) {
// Only provide updates if downloadListener is set (shouldNotify is true)
if (downloadListener != null) {
val eventDispatcher =
UIManagerHelper.getEventDispatcherForReactTag(context as ReactContext, id)
eventDispatcher?.dispatchEvent(createErrorEvent(UIManagerHelper.getSurfaceId(this), id, e))
}
}
}

public fun maybeUpdateView() {
Expand Down
23 changes: 17 additions & 6 deletions packages/rn-tester/js/examples/Image/ImageExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

'use strict';

import type {ImageProps} from 'react-native/Libraries/Image/ImageProps';
import type {LayoutEvent} from 'react-native/Libraries/Types/CoreEventTypes';

import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags';
Expand Down Expand Up @@ -227,12 +228,8 @@ type NetworkImageExampleState = {|
progress: $ReadOnlyArray<number>,
|};

type NetworkImageExampleProps = $ReadOnly<{|
source: ImageSource,
|}>;

class NetworkImageExample extends React.Component<
NetworkImageExampleProps,
ImageProps,
NetworkImageExampleState,
> {
state: NetworkImageExampleState = {
Expand All @@ -247,7 +244,7 @@ class NetworkImageExample extends React.Component<
) : (
<>
<Image
source={this.props.source}
{...this.props}
style={[styles.base, styles.visibleOverflow]}
onLoadStart={e => this.setState({loading: true})}
onError={e =>
Expand Down Expand Up @@ -960,6 +957,20 @@ exports.examples = [
);
},
},
{
title: 'Error Handler for Large Images',
render: function (): React.Node {
return (
<NetworkImageExample
resizeMethod="none"
source={{
// 15400x6940, 106,876,000 pixels, > 100MB bitmap
uri: 'https://upload.wikimedia.org/wikipedia/commons/2/2c/Mars_topography_%28MOLA_dataset%29_with_poles_HiRes.jpg',
}}
/>
);
},
},
{
title: 'Image Download Progress',
render: function (): React.Node {
Expand Down

0 comments on commit 9019e22

Please sign in to comment.