React-Native两个图片展示问题
项目需要客串开发react-native
的应用。壳已经准备好,加js
代码就行。虽然完全没有接触过APP开发的我有些手足无措,不过看到熟悉的生命周期和声明/调用方法,觉得这个活儿也能干。经过数天的“手册级”开发,最终成品看起来还不错,确实比内嵌html5页面顺畅太多了。下面撷取两个坑,证明一下,咱确实做过rn
开发。
iOS
图片不显示,onLoad
等函数不执行
图片设置宽度占满整屏,高度按比例转换,用top
属性,使图片垂直居中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const [imageHeight, setImageHeight] = useState(0); function imageLoad(value) { const {height, width} = value.nativeEvent.source; let tmpHeight = height / width * screenWidth; setImageHeight(tmpHeight); } <Image style={{height: imageHeight, top: (screenHeight - imageHeight) / 2}} source={{ uri }} onLoad={imageLoad} onLoadStart={() => console.log('onLoadStart')} onLoadEnd={() => console.log('onLoadEnd')} resizeMode="contain" /> |
最终android上显示是这样
iOS
,emmm…..点完没反应,排查发现,onLoad/onLoadEnd
等各种事件都没触发。
问题原因:如果给图片设置初始高度为0,在iOS下图片不会加载。如果设置为1,就正常,各种事件也会正常触发。
Android
相册,图片一多就OOM
导致app crash
如果要放置很多图片,又没有缩略图,全部加载就会报错(目测10几张,每张100k左右)
OOM(out of memory)
即内存不足。有两类log表现:
1 2 |
09-23 11:43:04.829 27468 27692 I ReactNativeJS: 失败原因:Pool hard cap violation? Hard cap = 201326592 Used size = 194146848 Free size = 0 Request size = 12160512 09-23 11:43:04.829 27468 27692 I ReactNativeJS: 加载图片失败 |
和
1 2 3 4 |
I/ReactNativeJS( 8935): emit: [Function: emit] } I/SurfaceFlinger( 431): FPS: 39 W/Adreno-GSL( 8935): <sharedmem_gpuobj_alloc:1845>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory E/Adreno-GSL( 8935): <gsl_memory_alloc_pure:1971>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed. |
实际手机效果差不多,均无法显示x张以后的图片。
由于写习惯了web方式的js,会认为手机端渲染过程也是一样,图片占用固定大小,只要不是超级分辨率,都没问题。但是,android不是。
一个看起来正常的Image
标签
1 |
<Image source={...}/> |
在Android手机上的图片处理,大致是如下过程:
jpg/png -> 解码器 -> Bitmap -> 放入内存供使用
也就是说,都会以Bitmap存储到内存中。那么这个Bitmap
会占用多大内存? 参考【 这里 】
以一张800 × 600分辨率的jpg,大小约135k的图片为例
Bitmap内存占用 ≈ 像素数据总大小 = 图片宽 × 图片高× (设备屏幕密度/资源目录对应dpi)^2 × 每个像素的字节大小
套用一下
1 |
17,280,000 = 800 × 600 × (480 / 160 )^2 × 4 |
即17M。
按照这个比例,大图片多来一些,APP OOM
导致崩溃是指日可待的。
直观的解决办法,压缩图片。
幸好,react-native
给出了一个resizeMethod
的解决办法,https://facebook.github.io/react-native/docs/image.html#resizemethod
对于远大于(2倍及以上)实际显示大小尺寸的图片,需要用resizeMethod='resize'
。其他情况使用'scale'
。
resize
和scale
的区别在于,resize
会存储一份更小的bitmap
,所以占用的空间更小。resize
过程要耗费一定的CPU。
scale
则是通过canvas
操作来使用硬件加速,bitmap
大小不变。硬件加速会耗费GPU。
这一部分请参考fresco 关于resizing的文档。
你成为大师路上的观望者和陪伴者。