Android 使用幀動(dòng)畫內(nèi)存溢出解決方案
Android 使用幀動(dòng)畫內(nèi)存溢出解決方案
最近在項(xiàng)目遇到的動(dòng)畫效果不好實(shí)現(xiàn),就讓UI切成圖,采用幀動(dòng)畫實(shí)現(xiàn)效果,但是在使用animation-list時(shí),圖片也就11張,每張圖片大概560k左右,結(jié)果內(nèi)存溢出,崩潰 了,自己用了三張都崩潰;拿代碼說;
1.anin_searh.xml
<?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/a1" android:duration="100"></item> <item android:drawable="@drawable/a2" android:duration="100"></item> <item android:drawable="@drawable/a4" android:duration="100"></item> <item android:drawable="@drawable/a5" android:duration="100"></item> <item android:drawable="@drawable/a6" android:duration="100"></item> <item android:drawable="@drawable/a7" android:duration="100"></item> <item android:drawable="@drawable/a8" android:duration="100"></item> <item android:drawable="@drawable/a9" android:duration="100"></item> <item android:drawable="@drawable/a10" android:duration="100"></item> <item android:drawable="@drawable/a11" android:duration="100"></item> </animation-list>
2.使用幀動(dòng)畫
search_scale_iv.setBackgroundResource(R.drawable.anim_search); AnimationDrawable drawable = (AnimationDrawable) search_scale_iv.getBackground(); drawable.start();
結(jié)果setBackgroundResource出現(xiàn)內(nèi)存溢出,這個(gè)方法其實(shí)獲取drawable時(shí)候,會(huì)消耗很多內(nèi)存,很容易內(nèi)存溢出,崩潰。
3.解決方法:在網(wǎng)上找了個(gè)類,處理,結(jié)果我使用11張560k大小圖片,沒有內(nèi)存溢出;
import android.content.Context; import android.content.res.XmlResourceParser; import android.graphics.BitmapFactory; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.widget.ImageView; import org.apache.commons.io.IOUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.List; /**** * 此工具類源于stack over flow * 原文鏈接:http://stackoverflow.com/questions/8692328/causing-outofmemoryerror-in-frame-by-frame-animation-in-android * 主要使用了BitmapFactory.decodeByteArray方法通過底層C來繪制圖片,有效防止OOM * 使用了第三方類庫:org.apache.commons.io.IOUtils,將Inputstream轉(zhuǎn)為byte字節(jié)數(shù)組 * *******/ public class MyAnimationDrawable { public static class MyFrame { byte[] bytes; int duration; Drawable drawable; boolean isReady = false; } public interface OnDrawableLoadedListener { public void onDrawableLoaded(List<MyFrame> myFrames); } // 1 /*** * 性能更優(yōu) * 在animation-list中設(shè)置時(shí)間 * **/ public static void animateRawManuallyFromXML(int resourceId, final ImageView imageView, final Runnable onStart, final Runnable onComplete) { loadRaw(resourceId, imageView.getContext(), new OnDrawableLoadedListener() { @Override public void onDrawableLoaded(List<MyFrame> myFrames) { if (onStart != null) { onStart.run(); } animateRawManually(myFrames, imageView, onComplete); } }); } // 2 private static void loadRaw(final int resourceId, final Context context, final OnDrawableLoadedListener onDrawableLoadedListener) { loadFromXml(resourceId, context, onDrawableLoadedListener); } // 3 private static void loadFromXml(final int resourceId, final Context context, final OnDrawableLoadedListener onDrawableLoadedListener) { new Thread(new Runnable() { @Override public void run() { final ArrayList<MyFrame> myFrames = new ArrayList<MyFrame>(); XmlResourceParser parser = context.getResources().getXml( resourceId); try { int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_DOCUMENT) { } else if (eventType == XmlPullParser.START_TAG) { if (parser.getName().equals("item")) { byte[] bytes = null; int duration = 1000; for (int i = 0; i < parser.getAttributeCount(); i++) { if (parser.getAttributeName(i).equals( "drawable")) { int resId = Integer.parseInt(parser .getAttributeValue(i) .substring(1)); bytes = IOUtils.toByteArray(context .getResources() .openRawResource(resId)); } else if (parser.getAttributeName(i) .equals("duration")) { duration = parser.getAttributeIntValue( i, 1000); } } MyFrame myFrame = new MyFrame(); myFrame.bytes = bytes; myFrame.duration = duration; myFrames.add(myFrame); } } else if (eventType == XmlPullParser.END_TAG) { } else if (eventType == XmlPullParser.TEXT) { } eventType = parser.next(); } } catch (IOException e) { e.printStackTrace(); } catch (XmlPullParserException e2) { // TODO: handle exception e2.printStackTrace(); } // Run on UI Thread new Handler(context.getMainLooper()).post(new Runnable() { @Override public void run() { if (onDrawableLoadedListener != null) { onDrawableLoadedListener.onDrawableLoaded(myFrames); } } }); } }).run(); } // 4 private static void animateRawManually(List<MyFrame> myFrames, ImageView imageView, Runnable onComplete) { animateRawManually(myFrames, imageView, onComplete, 0); } // 5 private static void animateRawManually(final List<MyFrame> myFrames, final ImageView imageView, final Runnable onComplete, final int frameNumber) { final MyFrame thisFrame = myFrames.get(frameNumber); if (frameNumber == 0) { thisFrame.drawable = new BitmapDrawable(imageView.getContext() .getResources(), BitmapFactory.decodeByteArray( thisFrame.bytes, 0, thisFrame.bytes.length)); } else { MyFrame previousFrame = myFrames.get(frameNumber - 1); ((BitmapDrawable) previousFrame.drawable).getBitmap().recycle(); previousFrame.drawable = null; previousFrame.isReady = false; } imageView.setImageDrawable(thisFrame.drawable); new Handler().postDelayed(new Runnable() { @Override public void run() { // Make sure ImageView hasn't been changed to a different Image // in this time if (imageView.getDrawable() == thisFrame.drawable) { if (frameNumber + 1 < myFrames.size()) { MyFrame nextFrame = myFrames.get(frameNumber + 1); if (nextFrame.isReady) { // Animate next frame animateRawManually(myFrames, imageView, onComplete, frameNumber + 1); } else { nextFrame.isReady = true; } } else { if (onComplete != null) { onComplete.run(); } } } } }, thisFrame.duration); // Load next frame if (frameNumber + 1 < myFrames.size()) { new Thread(new Runnable() { @Override public void run() { MyFrame nextFrame = myFrames.get(frameNumber + 1); nextFrame.drawable = new BitmapDrawable(imageView .getContext().getResources(), BitmapFactory.decodeByteArray(nextFrame.bytes, 0, nextFrame.bytes.length)); if (nextFrame.isReady) { // Animate next frame animateRawManually(myFrames, imageView, onComplete, frameNumber + 1); } else { nextFrame.isReady = true; } } }).run(); } } //第二種方法 /*** * 代碼中控制時(shí)間,但不精確 * duration = 1000; * ****/ public static void animateManuallyFromRawResource( int animationDrawableResourceId, ImageView imageView, Runnable onStart, Runnable onComplete, int duration) throws IOException, XmlPullParserException { AnimationDrawable animationDrawable = new AnimationDrawable(); XmlResourceParser parser = imageView.getContext().getResources() .getXml(animationDrawableResourceId); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_DOCUMENT) { } else if (eventType == XmlPullParser.START_TAG) { if (parser.getName().equals("item")) { Drawable drawable = null; for (int i = 0; i < parser.getAttributeCount(); i++) { if (parser.getAttributeName(i).equals("drawable")) { int resId = Integer.parseInt(parser .getAttributeValue(i).substring(1)); byte[] bytes = IOUtils.toByteArray(imageView .getContext().getResources() .openRawResource(resId));//IOUtils.readBytes drawable = new BitmapDrawable(imageView .getContext().getResources(), BitmapFactory.decodeByteArray(bytes, 0, bytes.length)); } else if (parser.getAttributeName(i) .equals("duration")) { duration = parser.getAttributeIntValue(i, 66); } } animationDrawable.addFrame(drawable, duration); } } else if (eventType == XmlPullParser.END_TAG) { } else if (eventType == XmlPullParser.TEXT) { } eventType = parser.next(); } if (onStart != null) { onStart.run(); } animateDrawableManually(animationDrawable, imageView, onComplete, 0); } private static void animateDrawableManually( final AnimationDrawable animationDrawable, final ImageView imageView, final Runnable onComplete, final int frameNumber) { final Drawable frame = animationDrawable.getFrame(frameNumber); imageView.setImageDrawable(frame); new Handler().postDelayed(new Runnable() { @Override public void run() { // Make sure ImageView hasn't been changed to a different Image // in this time if (imageView.getDrawable() == frame) { if (frameNumber + 1 < animationDrawable.getNumberOfFrames()) { // Animate next frame animateDrawableManually(animationDrawable, imageView, onComplete, frameNumber + 1); } else { // Animation complete if (onComplete != null) { onComplete.run(); } } } } }, animationDrawable.getDuration(frameNumber)); } }
這里需要導(dǎo)入jar,
import org.apache.commons.io.IOUtils;
4.然后通過上述類,來調(diào)用自己的動(dòng)畫xml,
MyAnimationDrawable.animateRawManuallyFromXML(R.drawable.anim_search, search_scale_iv, new Runnable() { @Override public void run() { // TODO onStart // 動(dòng)畫開始時(shí)回調(diào) log.d("","start"); } }, new Runnable() { @Override public void run() { // TODO onComplete // 動(dòng)畫結(jié)束時(shí)回調(diào) log.d("","end"); } });
這樣在使用幀動(dòng)畫時(shí),可以有效的適度防止內(nèi)存溢出,誰還有什么辦法,歡迎交流!
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
- Android動(dòng)畫之逐幀動(dòng)畫(Frame Animation)實(shí)例詳解
- Android 動(dòng)畫(View動(dòng)畫,幀動(dòng)畫,屬性動(dòng)畫)詳細(xì)介紹
- Android逐幀動(dòng)畫實(shí)現(xiàn)代碼
- Android之仿美團(tuán)加載數(shù)據(jù)幀動(dòng)畫
- Android動(dòng)畫之逐幀動(dòng)畫(Frame Animation)基礎(chǔ)學(xué)習(xí)
- Android幀動(dòng)畫、補(bǔ)間動(dòng)畫、屬性動(dòng)畫用法詳解
- Android編程之簡單逐幀動(dòng)畫Frame的實(shí)現(xiàn)方法
- Android 幀動(dòng)畫的實(shí)例詳解
- Android 逐幀動(dòng)畫創(chuàng)建實(shí)例詳解
- Android Studio實(shí)現(xiàn)幀動(dòng)畫
相關(guān)文章
Flutter中跨組件數(shù)據(jù)傳遞的方法總結(jié)
Flutter中的數(shù)據(jù)傳遞一般包括:父->子,子->父,父->父,也就是說嵌套時(shí)的傳遞以及跨頁面的傳遞,本文整理了三種我們通常使用的方法,需要的可以參考一下2023-06-06Android入門之RelativeLayout、FrameLayout用法分析
這篇文章主要介紹了Android入門之RelativeLayout、FrameLayout用法分析,需要的朋友可以參考下2014-08-08Flutter之自定義Dialog實(shí)現(xiàn)版本更新彈窗功能的實(shí)現(xiàn)
這篇文章主要介紹了Flutter之自定義Dialog實(shí)現(xiàn)版本更新彈窗功能的實(shí)現(xiàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07Android基于API的Tabs3實(shí)現(xiàn)仿優(yōu)酷t(yī)abhost效果實(shí)例
這篇文章主要介紹了Android基于API的Tabs3實(shí)現(xiàn)仿優(yōu)酷t(yī)abhost效果,結(jié)合完整實(shí)例形式分析了Android實(shí)現(xiàn)優(yōu)酷界面效果的相關(guān)技巧,需要的朋友可以參考下2015-12-12Android中Service和Activity相互通信示例代碼
在android中Activity負(fù)責(zé)前臺(tái)界面展示,service負(fù)責(zé)后臺(tái)的需要長期運(yùn)行的任務(wù)。下面這篇文章主要給大家介紹了關(guān)于Android中Service和Activity相互通信的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-09-09Android獲取當(dāng)前運(yùn)行的類名或者方法
這篇文章主要介紹了Android獲取當(dāng)前運(yùn)行的類名或者方法,涉及Android操作類與方法的技巧,需要的朋友可以參考下2015-05-05