Android使用SurfaceView代替AnimationDrawable播放多图帧动画,避免OOM和卡顿
来源:互联网 发布:织梦cms邀请码 编辑:程序博客网 时间:2024/05/16 10:03
关于Android帧动画
当我们在应用中需要使用帧动画的时候,最先想到的就是Android提供的AnimationDrawable了,但是如果你的帧动画中如果包含上百帧图片,此时再用AnimationDrawable就不是那么理想了。AnimationDrawable是使用一个Drawable数组来存储每一帧的图像的,当帧动画中包含非常多的图片时,AnimationDrawable在加载图片的过程中会有卡顿现象,很多图片被加载进内存,在一些低端设备上很容易造成OOM。
使用SurfaceView来实现帧动画的效果
最近的项目中需要用到大量的帧动画(多的高达200帧了),在低端手机一跑就OOM的,即使是旗舰机也是会有卡顿的现象。
第一个想到的解决办法就是用openGL来绘制了。
因为是直播的项目,直接在GLsurfaceView中绘制的,用OpenGL直接绘制一层Texture直接推流还省事。。只在主播端处理就行了,但是IOS那边都弄得差不多了,直接原生的不用处理也不会有什么异常什么的。。所以只能走即时通信了,想想IOS真幸福。。。
第二个就是使用Android自带的surfaceView了
好吧,第一个不行那就想到Android自带的surfaceView啦。我首先用不同的手机测试了下应用从本地decode一个bitmap的时间(png格式,414*736,大小在30-100k之间),因为帧动画的每帧不会太大,在性能好点的设备上基本保持在10-30ms之间(不推流基本上推流状态下10ms左右,推流状态下20左右),在性能稍差的设备中基本上也不会超过50ms,所以说是没什么大问题的。
实现
代码其实很简单,其实就是一个线程缓存,一个线程绘制,因为对fps要求不高,所以我只保存了5个bitmap在内存中,已经可以完全跟上绘制的节奏了,最后测试了下即使每帧50ms也是没有问题的。如果你的每帧图片比较大,可以适当的增加缓存数量。下面是代码,因为是写的demo,为了测试方便,图片时放在assets目录下的,每个帧动画中的图片放在一个单独的文件夹下就行了。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {private SurfaceView sv_main;private SurfaceHolder surfaceHolder;private SvCallback callback;private Map<String,Bitmap> bitmapCache =new HashMap<>();private String[] assets;private String folderName="daku";private AssetManager assetManager;private int totalCount;private Handler decodeHandler;private Thread decodeThread;private Button btn_am1;private Button btn_am2;private Button btn_am3;private Button btn_am4;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData();}private void initView() { sv_main = (SurfaceView) findViewById(R.id.sv_main); btn_am1 = (Button) findViewById(R.id.btn_am1); btn_am2 = (Button) findViewById(R.id.btn_am2); btn_am3 = (Button) findViewById(R.id.btn_am3); btn_am4 = (Button) findViewById(R.id.btn_am4); btn_am1.setOnClickListener(this); btn_am2.setOnClickListener(this); btn_am3.setOnClickListener(this); btn_am4.setOnClickListener(this);}private void initData(){ assetManager=getAssets(); surfaceHolder=sv_main.getHolder(); callback=new SvCallback(); sv_main.setZOrderOnTop(true); surfaceHolder.setFormat(PixelFormat.TRANSLUCENT); surfaceHolder.addCallback(callback);}@Overridepublic void onClick(View v) { if(callback.isDrawing) { callback.stopAnim(); } switch (v.getId()) { case R.id.btn_am1: folderName="daku"; break; case R.id.btn_am2: folderName="crow"; break; case R.id.btn_am3: folderName="bullshit"; break; case R.id.btn_am4: folderName="huabanyu"; break; } try { assets=assetManager.list(folderName); } catch (IOException e) { e.printStackTrace(); } totalCount=assets.length; startDecodeThread();}private class SvCallback implements SurfaceHolder.Callback{ private Canvas canvas; private Bitmap currentBitmap; private int position=0; public boolean isDrawing=false; private Thread drawThread; private Rect rect; @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { //full screen rect=new Rect(0,0,width,height); } private void drawBitmap() { if(position>=totalCount) { isDrawing=false; decodeHandler.sendEmptyMessage(-2); canvas=surfaceHolder.lockCanvas(); //clear surfaceView canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); surfaceHolder.unlockCanvasAndPost(canvas); return; } if(!bitmapCache.containsKey(assets[position])) { return; } currentBitmap=bitmapCache.get(assets[position]); decodeHandler.sendEmptyMessage(position); canvas=surfaceHolder.lockCanvas(rect); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); canvas.drawBitmap(currentBitmap,null,rect,null); surfaceHolder.unlockCanvasAndPost(canvas); currentBitmap.recycle(); position++; } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { stopAnim(); } public void startAnim() { isDrawing=true; position=0; drawThread=new Thread() { @Override public void run() { super.run(); while (isDrawing) { try { long now = System.currentTimeMillis(); drawBitmap(); //100ms draw one frame , you can change this time sleep(100 - (System.currentTimeMillis() - now)>0?100 - (System.currentTimeMillis() - now):0); } catch (InterruptedException e1) { e1.printStackTrace(); } } } }; drawThread.start(); } public void stopAnim() { isDrawing=false; position=0; bitmapCache.clear(); assets=null; //this is necessary drawThread.interrupt(); }}private void startDecodeThread(){ decodeThread=new Thread() { @Override public void run() { super.run(); Looper.prepare(); decodeHandler=new Handler(Looper.myLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if(msg.what==-2) { getLooper().quit(); return; } decodeBitmap(msg.what); } }; decodeBitmap(-1); Looper.loop(); } }; decodeThread.start();}private void decodeBitmap(int position){ try { if(position==-1) { for(int i=0;i<5;i++) { bitmapCache.put(assets[i], BitmapFactory.decodeStream(assetManager.open(folderName + "/" + assets[i]))); } callback.startAnim(); }else if(position==-2) { callback.stopAnim(); }else { if(position+5<=totalCount-1) { bitmapCache.remove(assets[position]); bitmapCache.put(assets[position+5],BitmapFactory.decodeStream(assetManager.open(folderName + "/" + assets[position+5]))); } } } catch (IOException e) { e.printStackTrace(); }}}
因为只是写了个demo,可能还有考虑不周的地方,欢迎一起探讨。完整的项目 Github
- Android使用SurfaceView代替AnimationDrawable播放多图帧动画,避免OOM和卡顿
- Android之视频播放使用MediaPlayer+SurfaceView代替ViedoView控件
- Android图片动画播放(AnimationDrawable)
- AnimationDrawable android 动画 初始化界面播放
- 使用SurfaceView播放gif动画
- Android:使用MediaPlayer和SurfaceView播放视频
- Android动画:AnimationDrawable资源的简单使用
- Android学习笔记进阶十一图片动画播放(AnimationDrawable)
- Android学习笔记进阶十一图片动画播放(AnimationDrawable)
- 解决使用AnimationDrawable无法播放动画的问题
- 解决使用AnimationDrawable无法播放动画的问题
- 解决使用AnimationDrawable无法播放动画的问题(转)
- 解决使用AnimationDrawable无法播放动画的问题
- AnimationDrawable 播放动画需要注意
- 图片动画播放(AnimationDrawable)
- android AnimationDrawable动画
- Android动画 - AnimationDrawable帧动画
- android播放帧动画OOM问题解决
- tomcat启动startup.bat一闪而过
- MongoDB常用操作命令有哪些?
- 27. Remove Element(Java)
- PropertyPlaceholderConfigurer类使用
- 表白专用
- Android使用SurfaceView代替AnimationDrawable播放多图帧动画,避免OOM和卡顿
- getchar()的灵活使用
- 同步示例
- 上课编程练习六
- NOR与NAND
- Android多线程ListView加载并缓冲图片
- 51nod 1072 威佐夫游戏
- 基于huffman的文件压缩
- 一步搞定无法审查元素