Android-日常遭遇-幀動畫oom處理篇
阿新 • • 發佈:2018-12-11
專案遭遇
實現幀動畫,我一開始想到的是直接通過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();
}
}
});
}
}