Download - FtnApp 的缩略图实践
FtnApp 的缩略图实践AyangXu
列表缩略图和预览大图
图片缓存策略(磁盘,内存)
耗时操作的应对(读写、压缩)
大图滑动的逐步调优
Agenda
两种缩略图的异同
列表缩略图
文件小(15-20k)
文件多(列表)
滑动的时候频繁读
高缓存命中
预览大图
文件大(200k+)
绘制耗时长
重用率低
内存控制要求更高
共同特点
平滑性要求高
多重检查:本地 ➜ 原图(压缩) ➜ 网络
过度状态(ICON / loadingView)
small
loadThumbnail
big
QMCache
thumb.db file
original file
web thumbnail
����
���
����
小图的缓存策略(磁盘)
small
loadThumbnail
QMCache
thumb.db
original file
web thumbnail
小图的缓存策略(磁盘)
全部小图保存到⼀一个 DB
启动的时候子线程加载(15k*100张)
滚动停止时,只更新 visibleCells
先更新内存,显示,再在后面更新 DB
不要在 cellForRowAtIndexPath 里做太多事情
图片缓存策略
小图的缓存策略(内存)
内存更新策略
缩略图只加不减
全部小图,包括默认的 ICON 都只读⼀一次
大图的缓存策略(内存)
NSCache
NSDiscardableContent / NSPurgeableData
不Copy Key,释放时机不可控
QMCache
NSMutableArray w/ NSMutableDictionary
自定义 Capacity,多了就删
耗时操作的应对
QMCache
thumb.db file
original file
web thumbnail
难点:
两种 IO 操作
两个网络操作
相互嵌套
本地 ➜ 原图 ➜ 网络
GCD VS OperationQueue
GCD
保留作用域,线程池
不好控制并发任务,不好取消
NSOperationQueue w/ NSBlockOperation
FIFO,封装了 GCD,可配置并发数,支持取消
保留还在队列里的 URL,避免重复的操作
快速滑动时,取消未开始的任务,优先留给当前页
[queue addOperation:[NSBlockOperation blockOperationWithBlock:^{dispatch_async(dispatch_get_main_queue(), ^{/*UI*/});
}]];
dispatch_async(dispatch_get_global_queue(0,0), ^{dispatch_async(dispatch_get_main_queue(),^{/*UI*/});
});
VS
大图滑动的逐步调优
大图滑动的基本实现
两层UIScrollView
1
2
⼀一次性准备ContentSize
{ numOfImages * (width + padding), height }
[previewImage renderMultipleImages:^(int page, downloadImageCallback callback) {asyncLoadImage(page, ^(UIImage *image) {callback(image);
});}
];
将读取逻辑扩展出去
评估工具
Core Animations Allocations
Instruments
大图滑动 V1
图片和 loadingView 分开两个数组
未渲染部分用 [NSNull null] 占位,提前创建好N个 loadingView
检查是否 [NSNull class] 来判定是否需要加载
scrollViewDidEndDecelerating 时加载
每次加载时创建 view,加载过的不会释放
大图滑动 V1
FPS 10-15
不断创建新的 view,不断释放
在 scrollViewDidEndDecelerating 才加载,太慢
进列表时间很长,初始化东西太多
大图滑动 V1.1
动态补充 loadingView
将绘制逻辑改到 scrollViewWillBeginDragging
缓存第二层的 zoomingScrollView
大图滑动 V1.1
进列表时间有所改善
所有第⼀一次翻动都很卡 FPS: 10-15
Cache 过的 view 不需重新创建 FPS: 20-30
大图滑动 V1.2
提前加载左右 N 页
判定是否到了 N 页,再进行加载
N = 5
大图滑动 V1.2
预加载左右 5 张,进列表时间又长了⼀一点
左右 5 张之内 FPS 30-40
第 6 张⼀一次性又要加载5张 FPS 10-20
大图滑动 V1.x
zoomingScrollView 基本不可重用
大图缓存的命中率很低
每⼀一次绘制都需要遍历三个N长数组
图片,loadingView,zoomingScrollView
每次翻动的时间跟列表长度相关 O(N)
创建后不移出 view,subviews 越来越多
大图滑动 V2
不采用单独的 loadingView 数组和 [NSNull null]
用 zoomingScrollView 来引用 loadingView 跟 image
View的缓存和回收
NSMutableSet
无序,NSObject 不重复
visiblePages / recyclePages
大图滑动 V2
scrollViewDidScroll
判定当前可见的页(<=2)
如果需要绘制,从 recyclePages 里 dequeue ⼀一个放到 visiblePages
回收不可见的页,从 visiblePages 里挪到 recyclePages
如果不需要绘制,什么都不做
大图滑动 V2
FPS 40-50
保持外层 UIScrollView 的 subviews,最多只有两个
减少不断创建新 view 的开销
减少 scrollViewDidScroll 时的遍历开销 O(1)
进化路线
滑动时的 FPS 15 => 45
滑动时的遍历:O(n) => O(1)
内存增长:
view 的创建和回收: N => 2
Next?
大图内存没有尽快释放
[obj release] 只是引用-1,并没有马上释放
采用小图策略,DB 保存等比小图
⼀一次加载到内存,内存只缓存小图 (20k * 100)
快速滑动时拉伸小图,模糊,但是快
scrollViewDidEndDecelerating 时绘制大图
view 不可见时马上清大图 image = nil;
And..
[NSURLConnection sendSynchronousRequest:returningResponse:error:]
所有网络数据都 load 到内存,并发下载三个,内存涨了 10M (Oh my!)
对可能的大图片,异步 IO + 文件流
ThanksQ&A