这几天在做动画的时候,遇到了一个OOM的问题,特此记录下来。

普通实现

实现一个帧动画,最先想到的就是用animation-list将全部图片按顺序放入,并设置时间间隔和播放模式。然后将该drawable设置给ImageView或Progressbar就OK了。

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false"> <item android:drawable="@drawable/smile0" android:duration="30"/>
<item android:drawable="@drawable/smile1" android:duration="30"/>
<item android:drawable="@drawable/smile2" android:duration="30"/>
<item android:drawable="@drawable/smile3" android:duration="30"/>
<item android:drawable="@drawable/smile4" android:duration="30"/>
</animation-list>

但是如果图片太多了,而且每张图片几百K的情况,就会出现OOM的问题。可以参考Stack Overflow上的这个问题Causing OutOfMemoryError in Frame by Frame Animation in Android

造成OOM的原因是因为帧动画从xml中读取图片的时候,一次性读取了所有的图片,并设置给ImageView,所以在图片数量过多和图片过大的时候,就会出现OOM。既然知道了原因,那么解决思路也很简单,就是在进行帧动画显示的时候,不要一下子读取所有的图片,而是需要谁就读取谁。

在github上找到一个例子,tigerjj/FasterAnimationsContainer,具体实现代码如下

public class FasterAnimationsContainer {
private class AnimationFrame{
private int mResourceId;
private int mDuration;
AnimationFrame(int resourceId, int duration){
mResourceId = resourceId;
mDuration = duration;
}
public int getResourceId() {
return mResourceId;
}
public int getDuration() {
return mDuration;
}
}
private ArrayList<AnimationFrame> mAnimationFrames; // list for all frames of animation
private int mIndex; // index of current frame private boolean mShouldRun; // true if the animation should continue running. Used to stop the animation
private boolean mIsRunning; // true if the animation prevents starting the animation twice
private SoftReference<ImageView> mSoftReferenceImageView; // Used to prevent holding ImageView when it should be dead.
private Handler mHandler; // Handler to communication with UIThread private Bitmap mRecycleBitmap; //Bitmap can recycle by inBitmap is SDK Version >=11 // Listeners
private OnAnimationStoppedListener mOnAnimationStoppedListener;
private OnAnimationFrameChangedListener mOnAnimationFrameChangedListener; private FasterAnimationsContainer(ImageView imageView, Context mContext) {
this.mContext = mContext;
init(imageView, mContext);
}; // single instance procedures
private static FasterAnimationsContainer sInstance; private Context mContext; public static FasterAnimationsContainer getInstance(ImageView imageView, Context mContext) {
if (sInstance == null)
sInstance = new FasterAnimationsContainer(imageView, mContext);
sInstance.mRecycleBitmap = null;
return sInstance;
} /**
* initialize imageview and frames
* @param imageView
* @param mContext
*/
public void init(ImageView imageView, Context mContext){
mAnimationFrames = new ArrayList<AnimationFrame>();
mSoftReferenceImageView = new SoftReference<ImageView>(imageView); mHandler = new Handler();
if(mIsRunning == true){
stop();
} mShouldRun = false;
mIsRunning = false; mIndex = -1;
} /**
* add a frame of animation
* @param index index of animation
* @param resId resource id of drawable
* @param interval milliseconds
*/
public void addFrame(int index, int resId, int interval){
mAnimationFrames.add(index, new AnimationFrame(resId, interval));
} /**
* add a frame of animation
* @param resId resource id of drawable
* @param interval milliseconds
*/
public void addFrame(int resId, int interval){
mAnimationFrames.add(new AnimationFrame(resId, interval));
} /**
* add all frames of animation
* @param resId resource id of drawable
* @param interval milliseconds
*/
public void addAllFrames(int resId, int interval){
int[] drawableIds = getData(resId);
for(int drawableId : drawableIds){
mAnimationFrames.add(new AnimationFrame(drawableId, interval));
}
} /**
* 从xml中读取帧数组
* @param resId
* @return
*/
private int[] getData(int resId){
TypedArray array = mContext.getResources().obtainTypedArray(resId); int len = array.length();
int[] intArray = new int[array.length()]; for(int i = 0; i < len; i++){
intArray[i] = array.getResourceId(i, 0);
}
array.recycle();
return intArray;
} /**
* remove a frame with index
* @param index index of animation
*/
public void removeFrame(int index){
mAnimationFrames.remove(index);
} /**
* clear all frames
*/
public void removeAllFrames(){
mAnimationFrames.clear();
} /**
* change a frame of animation
* @param index index of animation
* @param resId resource id of drawable
* @param interval milliseconds
*/
public void replaceFrame(int index, int resId, int interval){
mAnimationFrames.set(index, new AnimationFrame(resId, interval));
} private AnimationFrame getNext() {
mIndex++;
if (mIndex >= mAnimationFrames.size())
mIndex = 0;
return mAnimationFrames.get(mIndex);
} /**
* Listener of animation to detect stopped
*
*/
public interface OnAnimationStoppedListener{
public void onAnimationStopped();
} /**
* Listener of animation to get index
*
*/
public interface OnAnimationFrameChangedListener{
public void onAnimationFrameChanged(int index);
} /**
* set a listener for OnAnimationStoppedListener
* @param listener OnAnimationStoppedListener
*/
public void setOnAnimationStoppedListener(OnAnimationStoppedListener listener){
mOnAnimationStoppedListener = listener;
} /**
* set a listener for OnAnimationFrameChangedListener
* @param listener OnAnimationFrameChangedListener
*/
public void setOnAnimationFrameChangedListener(OnAnimationFrameChangedListener listener){
mOnAnimationFrameChangedListener = listener;
} /**
* Starts the animation
*/
public synchronized void start() {
mShouldRun = true;
if (mIsRunning)
return;
mHandler.post(new FramesSequenceAnimation());
} /**
* Stops the animation
*/
public synchronized void stop() {
mShouldRun = false;
} private class FramesSequenceAnimation implements Runnable{ @Override
public void run() {
ImageView imageView = mSoftReferenceImageView.get();
if (!mShouldRun || imageView == null) {
mIsRunning = false;
if (mOnAnimationStoppedListener != null) {
mOnAnimationStoppedListener.onAnimationStopped();
}
return;
}
mIsRunning = true; if (imageView.isShown()) {
AnimationFrame frame = getNext();
GetImageDrawableTask task = new GetImageDrawableTask(imageView);
task.execute(frame.getResourceId());
// TODO postDelayed after onPostExecute
mHandler.postDelayed(this, frame.getDuration());
}
}
} private class GetImageDrawableTask extends AsyncTask<Integer, Void, Drawable> { private ImageView mImageView; public GetImageDrawableTask(ImageView imageView) {
mImageView = imageView;
} @SuppressLint("NewApi")
@Override
protected Drawable doInBackground(Integer... params) {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
return mContext.getResources().getDrawable(params[0]);
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
if (mRecycleBitmap != null)
options.inBitmap = mRecycleBitmap;
mRecycleBitmap = BitmapFactory.decodeResource(mContext.getResources(), params[0], options);
BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(),mRecycleBitmap);
return drawable;
} @Override
protected void onPostExecute(Drawable result) {
super.onPostExecute(result);
if(result!=null) mImageView.setImageDrawable(result);
if (mOnAnimationFrameChangedListener != null)
mOnAnimationFrameChangedListener.onAnimationFrameChanged(mIndex);
} }

如何解决Android帧动画出现的内存溢出的更多相关文章

  1. 解决android加载图片时内存溢出问题

    尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过jav ...

  2. Android开发中如何解决加载大图片时内存溢出的问题

    Android开发中如何解决加载大图片时内存溢出的问题    在Android开发过程中,我们经常会遇到加载的图片过大导致内存溢出的问题,其实类似这样的问题已经屡见不鲜了,下面将一些好的解决方案分享给 ...

  3. 图片_ _Android有效解决加载大图片时内存溢出的问题 2

    Android有效解决加载大图片时内存溢出的问题 博客分类: Android Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或 setImageResource或 Bit ...

  4. android 帧动画的实现及图片过多时OOM解决方案(一)

    一,animation_list.xml中静态配置帧动画的顺序,如下: <?xml version="1.0" encoding="utf-8"?> ...

  5. Android帧动画实现,防OOM,比原生动画集节约超过十倍的资源

    2015年项目接到一个需求,实现一个向导动画,这个动画一共六十张图片,当时使用的是全志A33的开发(512的内存),通过使用Android的动画集实现,效果特别卡顿,然后想到这样的方式来实现,效果非常 ...

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

      帧动画——FrameAnimation 将一系列图片有序播放,形成动画的效果.其本质是一个Drawable,是一系列图片的集合,本身可以当做一个图片一样使用 在Drawable文件夹下,创建ani ...

  7. android 帧动画

    首先在res/drawable/name1.xml/定义一组图片集合: <?xml version="1.0" encoding="utf-8"?> ...

  8. android帧动画,移动位置,缩放,改变透明度等动画讲解

    1.苦逼的需求又来了,需要实现一些动画效果,第一个想到的是播放gif图片,但是这样会占包的资源,并且清晰度不高,于是想着程序实现,自己用帧动画+缩放+移动+透明度 实现了一些想要的效果,这里跟大家分享 ...

  9. Android帧动画笔记

    创建drawable资源文件,选择animation-list<?xml version="1.0" encoding="utf-8"?><a ...

随机推荐

  1. win2008 server 多IP配置

    本人服务器环境   win8 + phpstudy   一个服务器多个IP 以前都是用linux,买了几套源码结果都是win8server 服务器+phpstudy. 渐渐也就随大流了.懒的去琢磨 一 ...

  2. HBase与Zookeeper数据结构查询

    一.前言 最近一年了吧,总是忙于特定项目的业务分析和顶层设计,很少花时间和精力放到具体的技术细节,感觉除了架构理念和分析能力的提升,在具体技术层次却并没有多大的进步.因为一些原因,总被人问及一些技术细 ...

  3. ALGO-126_蓝桥杯_算法训练_水仙花

    问题描述 判断给定的三位数是否 水仙花 数.所谓 水仙花 数是指其值等于它本身 每位数字立方和的数.例 就是一个 水仙花 数. =++ 输入格式 一个整数. 输出格式 是水仙花数,输出"YE ...

  4. 【Graphite】Graphite常用函数使用

    使用Graphite进行sort排名 限制返回条数 aliasByNode(limit(sortByMaxima(summarize(EPIC.bm.00*.memory.memory.buffere ...

  5. Bitmap BitmapData

    var sp:Sprite=new Sprite(); sp.graphics.beginFill(0xffccdd); sp.graphics.drawRect(0,0,100,100); sp.g ...

  6. vue中滚动事件绑定的函数无法调用问题

    问题描述: 一个包含下拉加载的页面,刷新当前页然后滚动页面,能够正常触发滚动事件并调用回调函数,但是如果是进了某一个页面然后再进的该页面,滚动事件能够触发, 但是回调函数在滚动的时候只能被调用一次. ...

  7. bzoj 4866: [Ynoi2017]由乃的商场之旅

    设第i个字母的权值为1<<i,则一个可重集合可以重排为回文串,当且仅当这个集合的异或和x满足x==x&-x,用莫队维护区间内有多少对异或前缀和,异或后满足x==x&-x,这 ...

  8. Java学习——Applet写字符串(调字体)

    import java.awt.*; import java.applet.Applet; public class GUI2 extends Applet{ public void paint(Gr ...

  9. 学习笔记之pandas Foundations | DataCamp

    pandas Foundations | DataCamp https://www.datacamp.com/courses/pandas-foundations Many real-world da ...

  10. Android 使用自定义字体

    整个项目要使用第三方字体首先将字体文件放到assets文件夹下 因为整个项目要用第三方字体这里我重写了 TextView Button EditText 三个控件 以TextView 为例代码如下   ...