Migrate from react-native Image to expo-image to permanently resolve Android OutOfMemory crashes with massive local photo decodes

This commit is contained in:
2026-05-31 20:40:36 +02:00
parent a4a5406094
commit 63df4ae4f0
3 changed files with 25 additions and 7 deletions
+18
View File
@@ -16,6 +16,7 @@
"expo-build-properties": "~1.0.10", "expo-build-properties": "~1.0.10",
"expo-camera": "~17.0.10", "expo-camera": "~17.0.10",
"expo-file-system": "~19.0.23", "expo-file-system": "~19.0.23",
"expo-image": "~3.0.11",
"expo-image-manipulator": "~14.0.8", "expo-image-manipulator": "~14.0.8",
"expo-linear-gradient": "~15.0.8", "expo-linear-gradient": "~15.0.8",
"expo-media-library": "~18.2.1", "expo-media-library": "~18.2.1",
@@ -4671,6 +4672,23 @@
"react-native": "*" "react-native": "*"
} }
}, },
"node_modules/expo-image": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/expo-image/-/expo-image-3.0.11.tgz",
"integrity": "sha512-4TudfUCLgYgENv+f48omnU8tjS2S0Pd9EaON5/s1ZUBRwZ7K8acEr4NfvLPSaeXvxW24iLAiyQ7sV7BXQH3RoA==",
"license": "MIT",
"peerDependencies": {
"expo": "*",
"react": "*",
"react-native": "*",
"react-native-web": "*"
},
"peerDependenciesMeta": {
"react-native-web": {
"optional": true
}
}
},
"node_modules/expo-image-loader": { "node_modules/expo-image-loader": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-6.0.0.tgz", "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-6.0.0.tgz",
+1
View File
@@ -11,6 +11,7 @@
"expo-build-properties": "~1.0.10", "expo-build-properties": "~1.0.10",
"expo-camera": "~17.0.10", "expo-camera": "~17.0.10",
"expo-file-system": "~19.0.23", "expo-file-system": "~19.0.23",
"expo-image": "~3.0.11",
"expo-image-manipulator": "~14.0.8", "expo-image-manipulator": "~14.0.8",
"expo-linear-gradient": "~15.0.8", "expo-linear-gradient": "~15.0.8",
"expo-media-library": "~18.2.1", "expo-media-library": "~18.2.1",
+6 -7
View File
@@ -3,11 +3,11 @@ import {
StyleSheet, StyleSheet,
Text, Text,
View, View,
Image,
TouchableOpacity, TouchableOpacity,
ActivityIndicator, ActivityIndicator,
} from 'react-native'; } from 'react-native';
import ViewShot from 'react-native-view-shot'; import ViewShot from 'react-native-view-shot';
import { Image } from 'expo-image';
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
@@ -320,7 +320,7 @@ export default function PreviewScreen({
<View style={[styles.collageCanvas, styles.stripCanvas]}> <View style={[styles.collageCanvas, styles.stripCanvas]}>
<View style={styles.stripContent}> <View style={styles.stripContent}>
{photoUris.slice(-3).map((uri, idx) => ( {photoUris.slice(-3).map((uri, idx) => (
<Image key={idx} source={{ uri }} style={styles.stripImage} resizeMethod="resize" /> <Image key={idx} source={uri} style={styles.stripImage} contentFit="cover" />
))} ))}
</View> </View>
<View style={styles.collageFooter}> <View style={styles.collageFooter}>
@@ -334,7 +334,7 @@ export default function PreviewScreen({
<View style={[styles.collageCanvas, styles.gridCanvas]}> <View style={[styles.collageCanvas, styles.gridCanvas]}>
<View style={styles.gridContainer}> <View style={styles.gridContainer}>
{photoUris.slice(-4).map((uri, idx) => ( {photoUris.slice(-4).map((uri, idx) => (
<Image key={idx} source={{ uri }} style={styles.gridImage} resizeMethod="resize" /> <Image key={idx} source={uri} style={styles.gridImage} contentFit="cover" />
))} ))}
</View> </View>
<View style={styles.collageFooter}> <View style={styles.collageFooter}>
@@ -347,7 +347,7 @@ export default function PreviewScreen({
<View style={[styles.collageCanvas, styles.duoCanvas]}> <View style={[styles.collageCanvas, styles.duoCanvas]}>
<View style={styles.duoContainer}> <View style={styles.duoContainer}>
{photoUris.slice(-2).map((uri, idx) => ( {photoUris.slice(-2).map((uri, idx) => (
<Image key={idx} source={{ uri }} style={styles.duoImage} resizeMethod="resize" /> <Image key={idx} source={uri} style={styles.duoImage} contentFit="cover" />
))} ))}
</View> </View>
<View style={styles.collageFooter}> <View style={styles.collageFooter}>
@@ -359,10 +359,9 @@ export default function PreviewScreen({
return ( return (
<View style={styles.singleCanvas}> <View style={styles.singleCanvas}>
<Image <Image
source={{ uri: currentPhoto }} source={currentPhoto}
style={styles.singleImage} style={styles.singleImage}
resizeMode="cover" contentFit="cover"
resizeMethod="resize"
/> />
</View> </View>
); );