上线了一个图片 Feed 流,开发阶段一切正常,结果用户反馈:滑几屏就闪退。
Android 端尤其严重,Xcode Memory Graph 一看——好家伙,大量 UIImage / Bitmap 堆在内存里没释放。
一张 4000×3000 的照片,只显示 200×150,但 Android 直接把整张图解码到内存。按 ARGB_8888 算:4000 × 3000 × 4 = 48MB。
滑 10 张就是 480MB,直接 OOM。
iOS 好一些,UIImageView 会自动 Downsample,但大图一样吃内存。
而且 uri 变化时旧图片不会立即释放,Fresco 的内存缓存有 TTL,旧 Bitmap 要等 GC 或缓存淘汰才回收。列表快速滑动时,新图不断进来,旧图还没走——内存就爆了。
怎么解?四个方案,亲测有效:
① 限制图片解码尺寸(最推荐)
import { Image } from 'expo-image';const { width: SCREEN_WIDTH } = Dimensions.get('window');const IMAGE_WIDTH = (SCREEN_WIDTH - 48) / 2;const FeedItem = ({ item }) => (<Imagesource={{ uri: item.imageUrl }}style={{ width: IMAGE_WIDTH, height: IMAGE_WIDTH * 1.3 }}contentFit="cover"recyclingKey={item.id}/>);
② 用 expo-image 替代原生 Image
基于 SDWebImage(iOS) 和 Glide(Android),自动 Downsample、内存+磁盘双缓存、缓存淘汰策略、recyclingKey 复用机制,全给你安排好了。
npx expo install expo-image③ 控制 FlatList 渲染窗口
<FlatListwindowSize={5} // 默认 21,太大了maxToRenderPerBatch={10}removeClippedSubviews={true} // Android 尤其有效/>
④ 列表用缩略图,点击再加载大图
// 列表中用小图const thumbnailUrl = `${item.imageUrl}?w=400&q=75`;// 点击后才加载大图const fullUrl = item.imageUrl;
不要信任默认的图片处理,尤其是 Android。限制解码尺寸,用 expo-image,控制渲染窗口。
removeClippedSubviewswindowSize | ||
transition |
夜雨聆风