原创作者:唐子玄
https://juejin.im/post/5cd240f2e51d453afb40d83a
本文由作者授权发布。
继上篇用“SurfaceView逐帧解析 & 帧复用”优化了帧动画内存性能后,一个更复杂的问题浮出水面:帧动画时间性能。
这一篇试着让每帧素材大小 1MB 的帧动画流畅播放的同时不让内存膨胀。在整个优化过程中,综合运用了多线程、阻塞队列、消息机制、滑动窗口机制。也体悟到了计算机设计的中庸之道。
在此要感谢评论上一篇文章的掘友“小前锋”,是你的提问指引了我在这个方向上继续探索。
(ps:粗斜体表示引导方案逐步进化的关键点)
壹·SurfaceView逐帧解析 & 帧复用
简单回顾下上一篇的内容:原生帧动画在播放前解析所有帧,对内存压力大。SurfaceView可以精细地控制帧动画每一帧的绘制,在每一帧绘制前才解析当前帧,且解析后续帧时复用前帧内存空间。
遂整个过程在内存只申请了一帧图片大小的空间。下面罗列了一些关键代码:
基类:定义绘制框架
基类:定义绘制框架
public abstract class BaseSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
...
//绘制线程
private HandlerThread handlerThread;
private Handler handler;
@Override
public void surfaceCreated(SurfaceHolder holder) {
startDrawThread();
}
private void startDrawThread() {
handlerThread = new HandlerThread("SurfaceViewThread");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
handler.post(new DrawRunnable());
}
private class DrawRunnable implements Runnable {
@Override
public void run() {
try {
canvas = getHolder().lockCanvas();
//绘制一帧,包括解码+绘制帧
onFrameDraw(canvas);
} catch (Exception e) {
e.printStackTrace();
} finally {
getHolder().unlockCanvasAndPost(canvas);
onFrameDrawFinish();
}
//若onFrameDraw()执行超时,会导致下一帧的绘制被推后,预定的帧时间间隔不生效
handler.postDelayed(this, frameDuration);
}
}
protected abstract void onFrameDraw(Canvas canvas);
}
//帧动画绘制类:将绘制内容具体化为一张Bitmap
public class FrameSurfaceView extends BaseSurfaceView {
...
private BitmapFactory.Options options;
@Override
protected void onFrameDraw(Canvas canvas) {
clearCanvas(canvas);
if (!isStart()) {
return;
}
if (!isFinish()) {
//绘制一帧
drawOneFrame(canvas);
} else {
onFrameAnimationEnd();
}
}
private void drawOneFrame(Canvas canvas) {
//解析帧
frameBitmap = BitmapFactory.decodeResource(getResources(), bitmaps.get(bitmapIndex), options);
//复用帧
options.inBitmap = frameBitmap;
//绘制帧
canvas.drawBitmap(frameBitmap, srcRect, dstRect, paint);
bitmapIndex++;
}
...
贰·对比图片解析速度
对于素材在 100k 以下的帧动画,上一篇的逐帧解析方案完全能够胜任。但如果素材是几百k,时间性能就不如预期。
掘友“小前锋”问:“你的方案有测试过大图吗?比如1024*768px”
在逐帧解析SurfaceView上试了下这个大小的帧动画,虽然播放过程很连续,但 600ms 的帧动画被放成了 1s。因为预定义的每帧播放时间被解码时间拉长了。
有没有比BitmapFactory.decodeResource()更快的解码方式?
于是乎对比了各种图片解码的速度,其中包括BitmapFactory