Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 使用幀動畫內存溢出解決方案

Android 使用幀動畫內存溢出解決方案

編輯:關於Android編程

Android 使用幀動畫內存溢出解決方案

最近在項目遇到的動畫效果不好實現,就讓UI切成圖,采用幀動畫實現效果,但是在使用animation-list時,圖片也就11張,每張圖片大概560k左右,結果內存溢出,崩潰 了,自己用了三張都崩潰;拿代碼說;

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.使用幀動畫

search_scale_iv.setBackgroundResource(R.drawable.anim_search); 
    AnimationDrawable drawable = (AnimationDrawable) search_scale_iv.getBackground(); 
    drawable.start(); 

結果setBackgroundResource出現內存溢出,這個方法其實獲取drawable時候,會消耗很多內存,很容易內存溢出,崩潰。

3.解決方法:在網上找了個類,處理,結果我使用11張560k大小圖片,沒有內存溢出;

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轉為byte字節數組 
 * *******/ 
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 
  /*** 
   * 性能更優 
   * 在animation-list中設置時間 
   * **/ 
  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(); 
    } 
  } 
 
  //第二種方法 
  /*** 
   * 代碼中控制時間,但不精確 
   * 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)); 
  } 
 
} 

這裡需要導入jar,

import org.apache.commons.io.IOUtils;

4.然後通過上述類,來調用自己的動畫xml,

MyAnimationDrawable.animateRawManuallyFromXML(R.drawable.anim_search, 
            search_scale_iv, new Runnable() { 
 
              @Override 
              public void run() { 
                // TODO onStart 
                // 動畫開始時回調 
                log.d("","start"); 
                 
              } 
            }, new Runnable() { 
 
              @Override 
              public void run() { 
                // TODO onComplete 
                // 動畫結束時回調 
                log.d("","end"); 
                 
              } 
            }); 

這樣在使用幀動畫時,可以有效的適度防止內存溢出,誰還有什麼辦法,歡迎交流!

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved