Android 中加載幾百張圖片做幀動畫防止 OOM 的解決方案


Android 中加載幾百張圖片做幀動畫防止 OOM 的解決方案

最近,項目中有個需求:就是要做一個幀動畫,按理說這個是很簡單的!但是我能說這個幀動畫擁有幾百張圖片嗎?。。。。。。

填坑一 ---幀動畫

一開始我的想法是直接用幀動畫來做,可是我太天真了,當幀數放到 50 幾張的時候,已經在有些機器上奔潰了!所以這個方案否決!

填坑二 ---GIF動圖

雖然可以顯示,但是已經卡的我,已經不想看了,直接放棄

填坑三 ---視頻

在這里,我突然想到我可以直接把他做成一個小視頻啊,而且可以極限壓縮視頻。最終,視頻大小被壓縮到 500K 左右。此時已經基本可以滿足需求了,但是我們有好多類似的動畫,要求在每個動畫切換的時候要有銜接感,不能有突兀的感覺,所有在這里視頻就不能很好的完成任務了,所有再次放棄,已經淚牛滿面了!!!!

填坑四 --- SurfaceView + BitmapRegionDecoder +緩存

首先回答一下:為什么會想到這個解決方案?

  1. 首先在做幀動畫的時候,大約每幀之間的時間差值是 40ms 可以說速度非常快了,在如此快速的圖片切換上,自然而然的想到來了使用SurfaceView。
  2. 現在再來說說為什么想到要使用這個類 BitmapRegionDecoder .這個也是從我司游戲開發人員那兒得到的經驗?他們在做游戲的時候,游戲中的切圖都是放在一張大圖上的,然后在根據對應的 xml,json 文件,獲取相應的圖片,接着再來切圖。對此,我想能不能把所有的動圖都放到同一張的圖片上呢,之后在根據對應的描述文件,裁剪出我想要的圖片呢!所以就用到了 BitmapRegionDecoder. 它的作用是:於顯示圖片的某一塊矩形區域!之后,我在找設計人員商量一一下,把圖片在盡量的壓縮。之后從美工那兒獲取的信息是這樣的:
    json格式的描述文件:

{"frames": [

{
	"filename": "kidbot-正常閉眼0000",
	"frame": {"x":0,"y":0,"w":360,"h":300},
	"rotated": false,
	"trimmed": false,
	"spriteSourceSize": {"x":0,"y":0,"w":360,"h":300},
	"sourceSize": {"w":360,"h":300}
}
.....
}

png圖片:

圖片

接下來就好做了,解析 json 格式的文件,裁剪圖片。

  1. 最后說一下為什么使用緩存,其實很簡單,因為切換的頻率實在太高了,沒有必要每次都從圖片中裁剪,這里就把裁剪出來的 bitmap 緩存起來在用。從而介紹內存開銷!

最后給出代碼:

public class AnimView extends SurfaceView implements SurfaceHolder.Callback {
	private BitmapRegionDecoder bitmapRegionDecoder;
	private SurfaceHolder mHolder;
	private boolean isrunning = true;
	private AnimThread thread;
	private Paint mPaint;
	private int WIDTH = 0;
	private int HEIGHT = 0;
	private int state = -1;
	private boolean isstart = false;
	private boolean isblinkfirst = false;
	private int rate = 40;
	private int index = 0;
	private Matrix matrix;
	private Random rand;
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			isblinkfirst = true;
		};
	};
	private SparseArray<WeakReference<Bitmap>> weakBitmaps;
	private SparseArray<WeakReference<Bitmap>> cweakBitmaps;

	private BitmapFactory.Options options;

	public AnimView(Context context) {
		super(context);
		init();

	}

	public AnimView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public AnimView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init();
	}

	@SuppressLint("NewApi")
	private void init() {
		weakBitmaps = new SparseArray<WeakReference<Bitmap>>();
		cweakBitmaps = new SparseArray<WeakReference<Bitmap>>();
		mHolder = getHolder();
		mHolder.addCallback(this);
		mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		setState(FaceBean.BLINK);
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		matrix = new Matrix();
		float[] values = { -1f, 0.0f, 0.0f, 0.0f, 1f, 0.0f, 0.0f, 0.0f, 1.0f };
		matrix.setValues(values);
		WindowManager manger = (WindowManager) getContext().getSystemService(
				Context.WINDOW_SERVICE);
		DisplayMetrics displayMetrics = new DisplayMetrics();
		manger.getDefaultDisplay().getMetrics(displayMetrics);
		WIDTH = displayMetrics.widthPixels / 2;
		HEIGHT = displayMetrics.heightPixels / 2;
		rand = new Random();
		options = new Options();
		options.inPreferredConfig = Bitmap.Config.RGB_565;
	
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		handler.sendEmptyMessageDelayed(0, 1000 * (4 + rand.nextInt(4)));
		thread = new AnimThread();
		thread.start();
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {

	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		if (thread != null) {
			thread.stopThread();
		}
	}

	public class AnimThread extends Thread {

		@Override
		public void run() {
			super.run();
			SurfaceHolder holder = mHolder;
			while (isrunning) {
				Canvas canvas = holder.lockCanvas();
				if (canvas == null)
					continue;
				synchronized (AnimThread.class) {
					AnimBean.Frames frames;
					switch (state) {
					case FaceBean.BLINK:
						frames = KidbotRobotApplication.animBlink.getFrames()
								.get(index);
						if (frames.getFrame().getW() <= 0) {
						} else {
							Rect rect = new Rect(frames.getFrame().getX(),
									frames.getFrame().getY(), frames.getFrame()
											.getX()
											+ frames.getSourceSize().getW(),
									frames.getFrame().getY()
											+ frames.getSourceSize().getH());
							WeakReference<Bitmap> weakBitmap = weakBitmaps
									.get(index);
							Bitmap map = null;
							if (weakBitmap == null) {
								map = bitmapRegionDecoder.decodeRegion(rect,
										options);
								weakBitmaps.put(index,
										new WeakReference<Bitmap>(map));
							} else {
								map=weakBitmap.get();
								if (map == null) {
									map = bitmapRegionDecoder.decodeRegion(
											rect, options);
									weakBitmaps.put(index,
											new WeakReference<Bitmap>(map));
								}
							}
							if (map == null) {
								holder.unlockCanvasAndPost(canvas);
								continue;
							}
							mPaint.setXfermode(new PorterDuffXfermode(
									Mode.CLEAR));
							canvas.drawPaint(mPaint);
							mPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
							canvas.drawBitmap(map,
									(int) (WIDTH - (map.getWidth() * 1) - 150),
									(int) (HEIGHT - (map.getHeight() / 2)),
									mPaint);
							canvas.drawBitmap(map, (int) (WIDTH + 150),
									(int) (HEIGHT - (map.getHeight() / 2)),
									mPaint);

							if (index == 0) {

							}

							if (map.isRecycled()) {
								map.recycle();
							}

						}
						if (!isstart) {
							if (index < KidbotRobotApplication.animBlink
									.getFrames().size()) {
								index++;
								if (index == KidbotRobotApplication.animBlink
										.getFrames().size()) {
									index--;
									isstart = true;
									if (rand.nextInt(10) <= 2) {
										index = 1;
									}
								}
							} else {
								index--;
								isstart = true;
							}
						} else {
							if (index > 0) {
								index--;
								if (index == 0) {
									isstart = false;
								}
							} else {
								index++;
								isstart = false;
							}
						}
						if (!isblinkfirst) {
							index = 0;
						} else {
							if (index == KidbotRobotApplication.animBlink
									.getFrames().size() - 1) {
								isblinkfirst = false;
								index = 0;
								handler.sendEmptyMessageDelayed(0,
										1000 * (4 + rand.nextInt(4)));
							}
						}
						break;
					case FaceBean.ANGRY:
						frames = KidbotRobotApplication.animAngry.getFrames()
								.get(index);
						if (frames.getFrame().getW() <= 0) {
						} else {
							Rect rect = new Rect(frames.getFrame().getX(),
									frames.getFrame().getY(), frames.getFrame()
											.getX() + frames.getFrame().getW(),
									frames.getFrame().getH()
											+ frames.getFrame().getX());
							WeakReference<Bitmap> weakBitmap = weakBitmaps
									.get(index);
							Bitmap map = null;
							if (weakBitmap == null) {
								map = bitmapRegionDecoder.decodeRegion(rect,
										options);
								weakBitmaps.put(index,
										new WeakReference<Bitmap>(map));
							} else {
								map=weakBitmap.get();
								if (map == null) {
									map = bitmapRegionDecoder.decodeRegion(
											rect, options);
									weakBitmaps.put(index,
											new WeakReference<Bitmap>(map));
								}
							}
							if (map == null) {
								holder.unlockCanvasAndPost(canvas);
								continue;
							}
							mPaint.setXfermode(new PorterDuffXfermode(
									Mode.CLEAR));
							canvas.drawPaint(mPaint);
							mPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
							Bitmap dstbmp =null;
							weakBitmap=cweakBitmaps.get(index);
							if(weakBitmap==null){
								dstbmp = Bitmap.createBitmap(map, 0, 0,
										map.getWidth(), map.getHeight(),
										matrix, true);
								cweakBitmaps.put(index,
										new WeakReference<Bitmap>(dstbmp));
							}else{
								dstbmp=weakBitmap.get();
								if(dstbmp==null){
									dstbmp = Bitmap.createBitmap(map, 0, 0,
											map.getWidth(), map.getHeight(),
											matrix, true);
									cweakBitmaps.put(index,
											new WeakReference<Bitmap>(dstbmp));
								}
							}
							canvas.drawBitmap(
									map,
									frames.getSpriteSourceSize().getX()
											+ (int) (WIDTH
													- (map.getWidth() * 1) - 150),
									frames.getSpriteSourceSize().getY()
											+ (int) (HEIGHT - (map.getHeight() / 2)),
									mPaint);
							canvas.drawBitmap(dstbmp, frames
									.getSpriteSourceSize().getX()
									+ (int) (WIDTH + 150), frames
									.getSpriteSourceSize().getY()
									+ (int) (HEIGHT - (map.getHeight() / 2)),
									mPaint);
							if (dstbmp.isRecycled()) {
								dstbmp.recycle();
							}
							if (map.isRecycled()) {
								map.recycle();
							}
						}
						if (!isstart) {
							if (index < KidbotRobotApplication.animAngry
									.getFrames().size()) {
								index++;
								if (index == KidbotRobotApplication.animAngry
										.getFrames().size()) {
									index--;
									isstart = true;
								}
							} else {
								index--;
								isstart = true;
							}
						} else {
							if (index > 0) {
								index--;
								if (index == 0) {
									isstart = false;
								}
							} else {
								index++;
								isstart = false;
							}
						}
						break;
					case FaceBean.HAPPY:
						frames = KidbotRobotApplication.animHappy.getFrames()
								.get(index);
						if (frames.getFrame().getW() <= 0) {
						} else {
							Rect rect = new Rect(frames.getFrame().getX(),
									frames.getFrame().getY(), frames.getFrame()
											.getX()
											+ frames.getSourceSize().getW(),
									frames.getFrame().getY()
											+ frames.getSourceSize().getH());
							WeakReference<Bitmap> weakBitmap = weakBitmaps
									.get(index);
							Bitmap map = null;
							if (weakBitmap == null) {
								map = bitmapRegionDecoder.decodeRegion(rect,
										options);
								weakBitmaps.put(index,
										new WeakReference<Bitmap>(map));
							} else {
								map=weakBitmap.get();
								if (map == null) {
									map = bitmapRegionDecoder.decodeRegion(
											rect, options);
									weakBitmaps.put(index,
											new WeakReference<Bitmap>(map));
								}
							}
							if (map == null) {
								holder.unlockCanvasAndPost(canvas);
								continue;
							}
							mPaint.setXfermode(new PorterDuffXfermode(
									Mode.CLEAR));
							canvas.drawPaint(mPaint);
							mPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
							Bitmap dstbmp =null;
							weakBitmap=cweakBitmaps.get(index);
							if(weakBitmap==null){
								dstbmp = Bitmap.createBitmap(map, 0, 0,
										map.getWidth(), map.getHeight(),
										matrix, true);
								cweakBitmaps.put(index,
										new WeakReference<Bitmap>(dstbmp));
							}else{
								dstbmp=weakBitmap.get();
								if(dstbmp==null){
									dstbmp = Bitmap.createBitmap(map, 0, 0,
											map.getWidth(), map.getHeight(),
											matrix, true);
									cweakBitmaps.put(index,
											new WeakReference<Bitmap>(dstbmp));
								}
							}
							canvas.drawBitmap(
									map,
									frames.getSpriteSourceSize().getX()
											+ (int) (WIDTH
													- (map.getWidth() * 1) - 150),
									frames.getSpriteSourceSize().getY()
											+ (int) (HEIGHT - (map.getHeight() / 2)),
									mPaint);
							canvas.drawBitmap(dstbmp, frames
									.getSpriteSourceSize().getX()
									+ (int) (WIDTH + 150), frames
									.getSpriteSourceSize().getY()
									+ (int) (HEIGHT - (map.getHeight() / 2)),
									mPaint);
							// if (dstbmp.isRecycled()) {
							// dstbmp.recycle();
							// }
							// if (map.isRecycled()) {
							// map.recycle();
							// }

						}
						if (!isstart) {
							if (index < KidbotRobotApplication.animHappy
									.getFrames().size()) {
								index++;
								if (index == KidbotRobotApplication.animHappy
										.getFrames().size()) {
									index--;
									isstart = true;
								}
							} else {
								index--;
								isstart = true;
							}
						} else {
							if (index > 0) {
								index--;
								if (index == 0) {
									isstart = false;
								}
							} else {
								index++;
								isstart = false;
							}
						}
						break;
					case FaceBean.RESOLVE:
						break;
					case FaceBean.RISUS:
						break;
					case FaceBean.SEERIGHT:
						break;
					case FaceBean.SAD:
						frames = KidbotRobotApplication.animSad.getFrames()
								.get(index);
						if (frames.getFrame().getW() <= 0) {
						} else {
							Rect rect = new Rect(frames.getFrame().getX(),
									frames.getFrame().getY(), frames.getFrame()
											.getX()
											+ frames.getSourceSize().getW(),
									frames.getFrame().getY()
											+ frames.getSourceSize().getH());

							WeakReference<Bitmap> weakBitmap = weakBitmaps
									.get(index);
							Bitmap map = null;
							if (weakBitmap == null) {
								map = bitmapRegionDecoder.decodeRegion(rect,
										options);
								weakBitmaps.put(index,
										new WeakReference<Bitmap>(map));
							} else {
								map=weakBitmap.get();
								if (map == null) {
									map = bitmapRegionDecoder.decodeRegion(
											rect, options);
									weakBitmaps.put(index,
											new WeakReference<Bitmap>(map));
								}
							}
							if (map == null) {
								holder.unlockCanvasAndPost(canvas);
								continue;
							}
							mPaint.setXfermode(new PorterDuffXfermode(
									Mode.CLEAR));
							canvas.drawPaint(mPaint);
							mPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
							Bitmap dstbmp =null;
							weakBitmap=cweakBitmaps.get(index);
							if(weakBitmap==null){
								dstbmp = Bitmap.createBitmap(map, 0, 0,
										map.getWidth(), map.getHeight(),
										matrix, true);
								cweakBitmaps.put(index,
										new WeakReference<Bitmap>(dstbmp));
							}else{
								dstbmp=weakBitmap.get();
								if(dstbmp==null){
									dstbmp = Bitmap.createBitmap(map, 0, 0,
											map.getWidth(), map.getHeight(),
											matrix, true);
									cweakBitmaps.put(index,
											new WeakReference<Bitmap>(dstbmp));
								}
							}
							canvas.drawBitmap(
									map,
									frames.getSpriteSourceSize().getX()
											+ (int) (WIDTH
													- (map.getWidth() * 1) - 150),
									frames.getSpriteSourceSize().getY()
											+ (int) (HEIGHT - (map.getHeight() / 2)),
									mPaint);
							canvas.drawBitmap(dstbmp, frames
									.getSpriteSourceSize().getX()
									+ (int) (WIDTH + 150), frames
									.getSpriteSourceSize().getY()
									+ (int) (HEIGHT - (map.getHeight() / 2)),
									mPaint);
							if (dstbmp.isRecycled()) {
								dstbmp.recycle();
							}
							if (map.isRecycled()) {
								map.recycle();
							}
						}
						if (!isstart) {
							if (index < KidbotRobotApplication.animSad
									.getFrames().size()) {
								index++;
								if (index == KidbotRobotApplication.animSad
										.getFrames().size()) {
									index--;
									isstart = true;
								}
							} else {
								index--;
								isstart = true;
							}
						} else {
							if (index > 0) {
								index--;
								if (index == 0) {
									isstart = false;
								}
							} else {
								index++;
								isstart = false;
							}
						}
						break;
					default:
						break;
					}
				}
				holder.unlockCanvasAndPost(canvas);
				try {
					Thread.sleep(rate);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		public void stopThread() {
			isrunning = false;
			try {
				join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public synchronized void setRate(int rate) {
		this.rate = rate;
	}

	public int getState() {
		return this.state;
	}

	public synchronized void setState(int state) {
		// if (FaceBean.BLINK == this.state) {
		// while ((index != KidbotRobotApplication.animBlink.getFrames()
		// .size() - 1)) {
		// continue;
		// }
		// }
		cweakBitmaps.clear();
		weakBitmaps.clear();
		this.state = state;
		this.index = 0;

		switch (state) {
		case FaceBean.BLINK:
			try {
				bitmapRegionDecoder = BitmapRegionDecoder.newInstance(
						getContext().getAssets().open("kidbot_blink.png"),
						false);
			} catch (IOException e) {
				e.printStackTrace();
			}
			break;
		case FaceBean.ANGRY:
			try {
				bitmapRegionDecoder = BitmapRegionDecoder.newInstance(
						getContext().getAssets().open("kidbot_angry.png"),
						false);
			} catch (IOException e) {
				e.printStackTrace();
			}
			break;
		case FaceBean.HAPPY:
			try {
				bitmapRegionDecoder = BitmapRegionDecoder.newInstance(
						getContext().getAssets().open("kidbot_happy.png"),
						false);
			} catch (IOException e) {
				e.printStackTrace();
			}
			break;
		case FaceBean.RESOLVE:
			try {
				bitmapRegionDecoder = BitmapRegionDecoder.newInstance(
						getContext().getAssets().open("kidbot_blink.png"),
						false);
			} catch (IOException e) {
				e.printStackTrace();
			}
			break;
		case FaceBean.RISUS:
			try {
				bitmapRegionDecoder = BitmapRegionDecoder.newInstance(
						getContext().getAssets().open("kidbot_blink.png"),
						false);
			} catch (IOException e) {
				e.printStackTrace();
			}
			break;
		case FaceBean.SEERIGHT:
			break;
		case FaceBean.SAD:
			try {
				bitmapRegionDecoder = BitmapRegionDecoder.newInstance(
						getContext().getAssets().open("kidbot_sad.png"), false);
			} catch (IOException e) {
				e.printStackTrace();
			}
			break;
		}
	}

	public synchronized void setRunning(boolean isrunning) {
		this.isrunning = isrunning;
	}

	public synchronized void addIndex() {
		this.index++;
	}

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM