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"); 

              }
            });

这样在使用帧动画时,可以有效的适度防止内存溢出,谁还有什么办法,欢迎交流!

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Android编程之简单逐帧动画Frame的实现方法

    本文实例讲述了Android编程之简单逐帧动画Frame的实现方法.分享给大家供大家参考,具体如下: 1.逐帧动画 即是通过播放预先排序好的图片来实现动态的画面,感觉像是放电影. 2.实现步骤: ① 在工程里面导入要播放的图片.此简单例子中为start_icon1,2,3. ② 在工程res文件目录下新建一个anim文件夹,在里面新建一个start_animation.xml格式文件,此文件用来定义动画播放图片的顺序及每一张图片显示停留时间. 代码如下: <?xml version="1.

  • Android 逐帧动画创建实例详解

    Android 逐帧动画创建实例详解 前言: 我们看早期电影的时候,电影通常是一张一张播放,用我们现在专有名词来说,就是一帧帧来,安卓同样有这样动画效果的编排形式. 那么我们先定义逐帧动画xml文件 <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" an

  • Android之仿美团加载数据帧动画

    一:先来张效果图(这里是GIF动画,我就截屏的所有没有动画,实际是动的): 二:实现步骤: 1.xml布局 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" an

  • Android帧动画、补间动画、属性动画用法详解

    在安卓开发中,经常会使用到一些动画,那么在开发中,如何使用这些动画呢? 帧动画:不是针对View做出一些形状上的变化,而是用于播放一张张的图片,例如一些开机动画,类似于电影播放,使用的是AnimationDrawable来播放帧动画 res/drawable <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.androi

  • Android动画之逐帧动画(Frame Animation)实例详解

    本文实例分析了Android动画之逐帧动画.分享给大家供大家参考,具体如下: 在开始实例讲解之前,先引用官方文档中的一段话: Frame动画是一系列图片按照一定的顺序展示的过程,和放电影的机制很相似,我们称为逐帧动画.Frame动画可以被定义在XML文件中,也可以完全编码实现. 如果被定义在XML文件中,我们可以放置在/res下的anim或drawable目录中(/res/[anim | drawable]/filename.xml),文件名可以作为资源ID在代码中引用:如果由完全由编码实现,我

  • Android 动画(View动画,帧动画,属性动画)详细介绍

    0. 前言  Android动画是面试的时候经常被问到的话题.我们都知道Android动画分为三类:View动画.帧动画和属性动画. 先对这三种动画做一个概述: View动画是一种渐进式动画,通过图像的平移.缩放.旋转和透明度等各种渐进式变换完成动画效果. 帧动画是通过不停的切换图片实现动画效果. 属性动画是不停的改变对象的属性来实现动画效果.本文原创,转载请注明出处: http://blog.csdn.net/seu_calvin/article/details/52724655 1.  Vi

  • android 帧动画,补间动画,属性动画的简单总结

    帧动画--FrameAnimation 将一系列图片有序播放,形成动画的效果.其本质是一个Drawable,是一系列图片的集合,本身可以当做一个图片一样使用 在Drawable文件夹下,创建animation-list为根节点的资源文件 <animation-list android:oneshot="false"> <item android:drawable="@drawable/img1" android:duration="100

  • Android逐帧动画实现代码

    逐帧动画(Frame-by-frame Animations)顾名思义就是一帧接着一帧的播放图片,就像放电影一样.可以通过xml实现也可以通过java代码实现.逐帧动画适合实现比较简单的动画效果,如果要实现复杂动画不太建议使用逐帧动画. xml方式实现: step 1 : 在res/drawable目录下一个文件sample_animlist.xml: <?xml version="1.0" encoding="utf-8"?> <animatio

  • Android 帧动画的实例详解

    Android 帧动画的实例详解 对于 Android 帧动画 大体上可以理解成 一张张图片 按一定顺序切换, 这样当连续几张图是一组动画时,就可以连起来了看成是一个小电影,你懂得 好得,比就装到这里,下面开始进入正题,由于产品需求 需要做一个 声音喇叭动态切换的样式,我特么第一就想到是帧动画切换,然后就百度了一些资料,发现 真的, 现在这个网上太多的资料是 copy粘贴过来的, 一错全错,对于这种情况我只想说,made,一群垃圾, 所以今天我将带你们走进Android 正确帧动画地址. 第一步

  • Android动画之逐帧动画(Frame Animation)基础学习

    前言 在Android中,动画Animation的实现有两种方式:Tween Animation(补间动画)和Frame Animation(帧动画).渐变动画是通过对场景里的对象不断做图像变换(平移.缩放.旋转等)产生动画效果.帧动画则是通过顺序播放事先准备好的图像来产生动画效果,和电影类似. 下面我们就来学习下Android中逐帧动画的基础知识. 原理 : 人眼的"视觉暂留" 方式 : 1.在java代码中 ( new AnimationDrawable().addFrame(ge

随机推荐