Android 用surfaceview模拟帧动画的效果,解决帧动画的OOM问题

最近做的项目,客户临时要求改版,我真的是最烦这个,要求跟换主页面的背景,换上新的背景图,要求是动态的。

效果(我随便拿的五个图片做的gif):



方案:

帧动画方案:

        缺点:1.好像只能imageview才能播放帧动画 

                2.容易OOM(播三四张还行,播九十张以上,且,每张都在300k左右就有OOM问题)

                3.看到网上方案解决帧动画OOM,是一次播放十张,再往里面添十张,再播放。问题就有了,动画有一个                      添加十个图片的时间卡顿。


surfaceview方案:

         因为imageview要是以一张一张换,性能必定不如surfaceview。核心应该是

      canvas = surfaceHolder.lockCanvas(rect);
      canvas.drawBitmap(lruCache.get(folderName + "/" + assets[position]), null, rect, null);
        我把上百张的图片放在assets文件夹下,然后读取图片(这边可以做Lrucache优化),下边贴出我的代码:

        surfaceHolder = sv_main.getHolder();
        lruCache = new StringBitmapLruCache();
        surfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {

            }

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


                rect = new Rect(0, 0, width, height);

                //获取主题
                SharedPreferences sp = getSharedPreferences("setting", Context.MODE_PRIVATE);
                String theme = sp.getString("theme", "bgone");
                switch (theme) {
                    case "bgone":
                        folderName = "bgone";
                        break;
                    case "bgtwo":
                        folderName = "bgtwo";
                        break;
                    case "bgthree":
                        folderName = "bgthree";
                        break;
                }

                try {
                    assetManager = getAssets();
                    assets = assetManager.list(folderName);
                    totalCount = assets.length;
                    if (lruCache.get(folderName + "/" + assets[0]) == null) {
                        Bitmap bitmap = BitmapFactory.decodeStream(assetManager.open(folderName + "/" + assets[0]));
                        lruCache.put(folderName + "/" + assets[0], bitmap);
                    }
                    canvas = surfaceHolder.lockCanvas(rect);
                    canvas.drawBitmap(lruCache.get(folderName + "/" + assets[0]), null, rect, null);
                    holder.unlockCanvasAndPost(canvas);

                    if (!isFlag) {
                        startDecodeThread();
                        isFlag = true;
                    }


                } catch (IOException e) {
                    e.printStackTrace();
                }

                isFlag = true;


            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                RxUtils.unsubscribe(animationSub);
            }
        });

 private void startDecodeThread() {

        RxUtils.unsubscribe(animationSub);
        animationSub = Observable.interval(200, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io()).subscribe(new Subscriber() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(Long aLong) {
                Logger.i("DDSSK::" + position);
                if (position >= totalCount) {
                    position = 0;
                }

                canvas = surfaceHolder.lockCanvas(rect);
                // if (canvas != null) {

                canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                if (lruCache.get(folderName + "/" + assets[position]) == null) {
                    try {
                        Bitmap bitmap = BitmapFactory.decodeStream(assetManager.open(folderName + "/" + assets[position]));
                        lruCache.put(folderName + "/" + assets[position], bitmap);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                canvas.drawBitmap(lruCache.get(folderName + "/" + assets[position]), null, rect, null);
                surfaceHolder.unlockCanvasAndPost(canvas);


              /*  try {
                    Bitmap bitmap = BitmapFactory.decodeStream(assetManager.open(folderName + "/" + assets[0]));
                    canvas.drawBitmap(bitmap, null, rect, null);
                    surfaceHolder.unlockCanvasAndPost(canvas);

                } catch (IOException e) {
                    e.printStackTrace();
                }*/

                position++;
            }
        });
    }

好,下面讲一下我遇到的坑

1.surfaceview准备的时候,界面是一片黑。所以在surfacechanged的时候我要展示动画的第一张图,这样就不黑了

2.canvas为空?因为canvas是从surfaceview获得的,如果surfaceview没有准备好,它绝对为空

3.如果界面跳转再返回,surfaceview还会创建,但是我点了黑屏再亮屏,surfaceview就不会在创建,动画就播不了,所以要写一个开关,在onresume里写代码触发动画

4.surfaceview的优化,因为播放太多的图片,占用太多资源,导致其他的组件超卡。因为surfaceview可以在子线程跟新UI,所以我们可以起线程轮播图


附件:

package utils;

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

/**
 * Created by Administrator on 2017/6/23 0023.
 */

public class StringBitmapLruCache extends LruCache {
    public StringBitmapLruCache() {
        // 构造方法传入当前应用可用最大内存的八分之一
        super((int) (Runtime.getRuntime().maxMemory() / 1024 / 8));
    }

    @Override
    // 重写sizeOf方法,并计算返回每个Bitmap对象占用的内存
    protected int sizeOf(String key, Bitmap value) {
        return value.getByteCount() / 1024;
    }

    @Override
    // 当缓存被移除时调用,第一个参数是表明缓存移除的原因,true表示被LruCache移除,false表示被主动remove移除,可不重写
    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap
            newValue) {
        super.entryRemoved(evicted, key, oldValue, newValue);
    }

    @Override
    // 当get方法获取不到缓存的时候调用,如果需要创建自定义默认缓存,可以在这里添加逻辑,可不重写
    protected Bitmap create(String key) {
        return super.create(key);
    }
}

结束语:虽然这个需求是实现了,但造成了另外一个问题,apk过大,因为播放一个主题,就是至少90图,每张300k。我要实现三个主题,那就是五六十M。于是我就在想,当初要是弄成视频文件,我播放视屏的话,既解决了OOM也解决了APK过大的问题。







你可能感兴趣的:(Android)