乐于分享
好东西不私藏

滑10张图App就崩了

滑10张图App就崩了

上线了一个图片 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 { widthSCREEN_WIDTH } = Dimensions.get('window');const IMAGE_WIDTH = (SCREEN_WIDTH - 48) / 2;const FeedItem = ({ item }) => (  <Image    source={{ uri: item.imageUrl }}    style={{ width: IMAGE_WIDTHheight: IMAGE_WIDTH * 1.3 }}    contentFit="cover"    recyclingKey={item.id}  />);

② 用 expo-image 替代原生 Image

基于 SDWebImage(iOS) 和 Glide(Android),自动 Downsample、内存+磁盘双缓存、缓存淘汰策略、recyclingKey 复用机制,全给你安排好了。

npx expo install expo-image

③ 控制 FlatList 渲染窗口

<FlatList  windowSize={5}              // 默认 21太大了  maxToRenderPerBatch={10}  removeClippedSubviews={true}  // Android 尤其有效/>

④ 列表用缩略图,点击再加载大图

// 列表中用小图const thumbnailUrl = `${item.imageUrl}?w=400&q=75`;// 点击后才加载大图const fullUrl = item.imageUrl;

不要信任默认的图片处理,尤其是 Android。限制解码尺寸,用 expo-image,控制渲染窗口。

问题
原因
解决方案
Android OOM
大图全尺寸解码
使用 expo-image + 限制尺寸
列表滑动卡顿
内存缓存不释放
removeClippedSubviews
 + windowSize
iOS 偶尔闪退
UIImageView 缓存未及时回收
缩略图策略 + recyclingKey
图片闪烁
快速滑动时重复加载
transition
 动画 + 磁盘缓存