Frame animation memory OOM? nonexistent! - SurfaceView analytic frame by frame - https://juejin.im/post/5ccc1dcbe51d456e8b07ddf4
. Play Frame Animation by SurfaceView the Get RID of OOM Due to Complex Frame Animation- https://github.com/wisdomtl/FrameSurfaceView/tree/master
Android It provides AnimationDrawable used to achieve frame animation. Before the beginning of the animation, images of all the frames are parsed and take up memory, once more complex animation frames are more likely to occur on low OOM phone. Even OOM does not occur, it can also cause considerable pressure on memory.
- 原生帧动画AnimationDrawable
AnimationDrawable drawable = new AnimationDrawable();
drawable.addFrame(getDrawable(R.drawable.frame1), frameDuration);
drawable.addFrame(getDrawable(R.drawable.frame2), frameDuration);
drawable.addFrame(getDrawable(R.drawable.frame3), frameDuration);
drawable.addFrame(getDrawable(R.drawable.frame4), frameDuration);
drawable.setOneShot(true);
ImageView ivFrameAnim = ((ImageView) findViewById(R.id.frame_anim));
ivFrameAnim.setImageDrawable(drawable);
drawable.start();
Is there any way to get the data frame of the animation frame by frame to load, but not loaded into memory all at once? SurfaceView provides this capability.
To display a required calculation and rendering undergo two processes, CPU took the image data is calculated and written into the frame memory, and then call the OpenGL command-memory data stored in the image rendered in the GPU Buffer, every display device Buffer acquire images from a certain time and displayed.
- Frame SurfaceView parsing custom templates SurfaceView Code:
public abstract class BaseSurfaceView the extends SurfaceView the implements SurfaceHolder.Callback {
public static int DEFAULT_FRAME_DURATION_MILLISECOND Final = 50;
// the frame data for calculating the thread
Private and HandlerThread HandlerThread;
Private Handler Handler;
// the frame refresh frequency
Private frameDuration int = DEFAULT_FRAME_DURATION_MILLISECOND;
// draw a canvas frame
Private the canvas canvas;
Private isAlive Boolean;
public BaseSurfaceView(Context context) {
super(context);
init();
}
void the init protected () {
getHolder () the addCallback (the this);.
// a transparent background, the background is black or SurfaceView
setBackgroundTransparent ();
}
private void setBackgroundTransparent() {
getHolder().setFormat(PixelFormat.TRANSLUCENT);
setZOrderOnTop(true);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isAlive = true;
startDrawThread();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
stopDrawThread();
isAlive = false;
}
// Draw thread stop frame
Private void stopDrawThread () {
handlerThread.quit ();
Handler = null;
}
//启动帧绘制线程
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 () {
IF (! IsAlive) {
return;
}
the try {
.. 1 // Get the canvas
Canvas = getHolder () lockCanvas ();.
. 2 // draw a
onFrameDraw (Canvas);
} the catch ( E Exception) {
e.printStackTrace ();
} {the finally
. //. 3 frame data submitted
getHolder () unlockCanvasAndPost (Canvas);.
//. 4 a rendering end.
onFrameDrawFinish ();
}
// stop their pushed to draw thread's message queue to achieve a frame refresh
handler.postDelayed(this, frameDuration);
}
}
protected abstract void onFrameDrawFinish();
protected abstract void onFrameDraw(Canvas canvas);
}
- 逐帧解析 & 及时回收
public class FrameSurfaceView extends BaseSurfaceView {
public static final int INVALID_BITMAP_INDEX = Integer.MAX_VALUE;
private List<Integer> bitmaps = new ArrayList<>();
private Bitmap frameBitmap;
private int bitmapIndex = INVALID_BITMAP_INDEX;
private Paint paint = new Paint();
private BitmapFactory.Options options;
private Rect srcRect;
private Rect dstRect = new Rect();
public void setDuration(int duration) {
int frameDuration = duration / bitmaps.size();
setFrameDuration(frameDuration);
}
public void setBitmaps(List<Integer> bitmaps) {
if (bitmaps == null || bitmaps.size() == 0) {
return;
}
this.bitmaps = bitmaps;
getBitmapDimension(bitmaps.get(0));
}
private void getBitmapDimension(Integer integer) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(this.getResources(), integer, options);
defaultWidth = options.outWidth;
defaultHeight = options.outHeight;
srcRect = new Rect(0, 0, defaultWidth, defaultHeight);;
}
public FrameSurfaceView(Context context) {
super(context);
}
@Override
protected void the init () {
the super.init ();
// parse Bitmap parameter defined variable type, in order to reuse Bitmap
Options new new BitmapFactory.Options = ();
options.inMutable = to true;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
dstRect.set(0, 0, getWidth(), getHeight());
}
@Override
protected int getDefaultWidth() {
return defaultWidth;
}
@Override
protected int getDefaultHeight() {
return defaultHeight;
}
@Override
protected void onFrameDrawFinish () {
// for each frame drawing After completion of recovery is no longer
// Recycle ();
}
public void recycle() {
if (frameBitmap != null) {
frameBitmap.recycle();
frameBitmap = null;
}
}
@Override
protected void onFrameDraw(Canvas canvas) {
clearCanvas(canvas);
if (!isStart()) {
return;
}
if (!isFinish()) {
drawOneFrame(canvas);
} else {
onFrameAnimationEnd();
}
}
void drawOneFrame Private (the Canvas Canvas) {
frameBitmap = BitmapUtil.decodeOriginBitmap (getResources (),
// re spend a Bitmap memory
bitmaps.get (bitmapIndex), Options);
options.inBitmap = frameBitmap;
Canvas.drawBitmap (frameBitmap, srcRect, dstRect, Paint);
bitmapIndex ++;
}
private void onFrameAnimationEnd() {
reset();
}
private void reset() {
bitmapIndex = INVALID_BITMAP_INDEX;
}
private boolean isFinish() {
return bitmapIndex >= bitmaps.size();
}
private boolean isStart() {
return bitmapIndex != INVALID_BITMAP_INDEX;
}
public void start() {
bitmapIndex = 0;
}
private void clearCanvas(Canvas canvas) {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
}
}