1. 程式人生 > >Android-日常遭遇-幀動畫oom處理篇

Android-日常遭遇-幀動畫oom處理篇

專案遭遇

實現幀動畫,我一開始想到的是直接通過animation-list將全部圖片按順序放入,並設定時間間隔和播放模式。然後將該drawable設定給ImageView,然後就可以了

<?xml version="1.0" encoding="utf-8"?>
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:drawable="@drawable/score_6" android:duration="80"/>
<item android:drawable="@drawable/score_9" android:duration="80"/>
<item android:drawable="@drawable/score_13" android:duration="80"/>
<item android:drawable="@drawable/score_19" android:duration="80"/>
<item android:drawable="@drawable/score_22" android:duration="80"/>
<item android:drawable="@drawable/score_23" android:duration="80"/>
<item android:drawable="@drawable/score_24" android:duration="80"/>
</animation-list>
後來事情也正如所料的一樣,完美加載出來了
AnimationDrawable animationDrawable;
if (imageView.getDrawable() == null) {
     imageView.setImageResource(R.drawable.score_anim);
     animationDrawable =(AnimationDrawable)imageView.getDrawable();
}
animationDrawable.start();//開始
animationDrawable.stop();//結束 

但是到了後面,我工程的多個地方需要載入動畫,發現直接oom.檢視原始碼發現,是一次匯入全部圖片才會產生.

然後對原始碼的動畫載入機制進行優化.

然後還做了一系列的封裝.這其中,into的時候,會預設載入第一幀,在initDrawableList的時候,第一個引數就是動畫的時間.第二個引數是動畫資源的陣列.我做了可變引數的處理

AnimationManager animationManager = AnimationManager.getInstance().initDrawableList(80,
                                R.drawable.score_0,
                                R.drawable.score_1,
                                R.drawable.score_2,
                                R.drawable.score_3,
                                R.drawable.score_4,
                                R.drawable.score_5,
                                R.drawable.score_6,
                                R.drawable.score_7,
                                R.drawable.score_8,
                                R.drawable.score_9,
                                R.drawable.score_11,
                                R.drawable.score_12,
                                R.drawable.score_13,
                                R.drawable.score_14,
                                R.drawable.score_15,
                                R.drawable.score_16,
                                R.drawable.score_17,
                                R.drawable.score_18,
                                R.drawable.score_19,
                                R.drawable.score_20,
                                R.drawable.score_21,
                                R.drawable.score_22,
                                R.drawable.score_23,
                                R.drawable.score_24,
                                R.drawable.score_25
                        ).into(close);
                        animationManager.start();

這樣就非常完美的播放出幀動畫,並且不會有記憶體溢位的出現.這邊採用了rxjava處理,如果專案不使用,則可以改成執行緒和切換執行緒的處理方式.

附上AnimationManager原始碼

public class AnimationManager {

    private static AnimationManager sAnimationManager;
    private Handler mHandler = new Handler(Looper.getMainLooper());
    private long  fps;
    private int[] res;
    private int   oneImage;
    private int count  = 1;
    private int length = 0;
    private ImageView  intoView;
    private Bitmap     nextBitmap;
    private Bitmap     currentBitmap;
    private Disposable mSubscribe;

    private AnimationManager() {
    }

    public static AnimationManager getInstance() {
        if (sAnimationManager == null) {
            sAnimationManager = new AnimationManager();
        }
        return sAnimationManager;
    }

    public AnimationManager initDrawableList(long fps, int... Res) {
        this.fps = fps;
        this.res = Res;
        count = 1;
        if (Res.length == 0) {
            throw new RuntimeException("不能是空陣列");
        }
        this.oneImage = Res[0];
        length = Res.length;
        return this;
    }

    public AnimationManager into(ImageView view) {
        this.intoView = view;
        // 載入第一張
        Observable.just(oneImage)
                .observeOn(Schedulers.io())
                .map(new Function<Integer, Bitmap>() {
                    @Override
                    public Bitmap apply(Integer integer) throws Exception {
                        BitmapFactory.Options opts = getOptions(integer);
                        // 載入下一張
                        if (res.length > 1) {
                            nextBitmap = BitmapFactory.decodeResource(intoView.getResources(), res[count++], opts);
                        }
                        return BitmapFactory.decodeResource(intoView.getResources(), integer, opts);
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Bitmap>() {
                    @Override
                    public void accept(Bitmap bitmap) throws Exception {
                        intoView.setImageBitmap(bitmap);
                    }
                });
        return this;
    }

    @NonNull
    private BitmapFactory.Options getOptions(Integer integer) {
        /**
         * 先獲取圖片的和IamgeView各自的寬高
         */
        BitmapFactory.Options opts = new BitmapFactory.Options();
        //給opts配置只獲取圖片的元資料
        opts.inJustDecodeBounds = true;
        //注意:由於配置了opts並且是僅僅獲取圖片邊界的屬性,因此該方法返回的物件永遠為null
        BitmapFactory.decodeResource(intoView.getResources(), integer, opts);
        //從opts物件上獲取圖片的寬高
        int width  = opts.outWidth;
        int height = opts.outHeight;

        int width1 = ScreenUtils.getScreenWidth(intoView.getContext());
        int height1 = ScreenUtils.getScreenHeight(intoView.getContext());

        int sampleSize = Math.max(width / width1, height / height1);


        opts.inJustDecodeBounds = false;//必須配置為載入圖片,而不是僅僅獲取圖片的尺寸
        opts.inSampleSize = sampleSize; //配置等比例縮放的倍數
        return opts;
    }

    public synchronized void start() {
        mSubscribe = Observable.interval(fps, TimeUnit.MILLISECONDS)
                .observeOn(Schedulers.io())
                .map(new Function<Long, Boolean>() {
                    @Override
                    public Boolean apply(Long aLong) throws Exception {
                        count++;
                        if (nextBitmap != null) {
                            currentBitmap = nextBitmap;
                        }
                        if (count > res.length - 1) {
                            return false;
                        }
                        BitmapFactory.Options opts = getOptions(res[count]);
                        nextBitmap = BitmapFactory.decodeResource(intoView.getResources(), res[count], opts);
                        return true;
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean flag) throws Exception {
                        if (flag) {
                            intoView.setImageBitmap(currentBitmap);
                        } else {
                            mSubscribe.dispose();
                        }

                    }
                });

    }
}