这几天在做动画的时候,遇到了一个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. WASAPI、DirectSound/DS、WaveOut、Kernel Streaming/KS

    先放结论: ASIO:硬件支持+对应驱动程序 DS:兼容性最好,一般也是默认的. WASAPI:是Vista之后的,较佳选择输出方式. 再来详细看: ASIO.WDM都是指音频通道,就是音频数据走的路 ...

  2. url参数 加密

    加密 url +? btoa(param) 解密 url + ?atob(param)

  3. python的Socket网络编程

    计算机网络: 多台独立的计算机用网络通信设备连接起来的网络.实现资源共享和数据传递.比如,我们之前的学过的知识可以将D盘的一个文件传到C盘,但如果你想从你的电脑传一个文件到我的电脑上目前是做不到的; ...

  4. MySQL中int(M)和tinyint(M)数值类型中M值的意义

    在一开始接触MySQL数据库时,对于int(M)及tinyint(M)两者数值类型后面的M值理解是最多能够插入数据库中的值不能大于M: 后来工作后,也是一边学习一边使用,之后的理解是其中的M的意思是插 ...

  5. S型顺序遍历二叉树(c++实现)

    //1.s型顺序访问二叉树,默认先左后右:利用两个栈来实现:如果先右后左的话,改变一下入栈的顺序就行 //2.注意s1 s2插入栈的顺序是不同的 void S_LevelOrderPrint(Tree ...

  6. Python 里面什么时候用一个=,什么时候用两个=

    赋值用=                                                                   i != j != k   >>>> ...

  7. 微信小程序客服消息使用

    客服消息使用 为丰富小程序的服务能力,提高服务质量,微信为小程序提供客服消息能力,以便小程序用户可以方便快捷地与小程序服务提供方进行沟通. xiaokefu.com.cn 功能介绍 用户可使用小程序客 ...

  8. Hive的UDF是什么?

    首先我们学习hadoop的时候,为了让我们不太会java语言但是对SQL很熟悉的工程师能够操作基本的mapreduce计算过程,Hive被设计出来了.Hive就好比是hadoop在执行MR(mapre ...

  9. spring4.0之八:Groovy DSL

    4.0的一个重要特征就是完全支持Groovy,Groovy是Spring主导的一门基于JVM的脚本语言(动态语言).在spring 2.x,脚本语言通过 Java scripting engine在S ...

  10. Java-Runoob-高级教程-实例-方法:08. Java 实例 – break 关键字用法

    ylbtech-Java-Runoob-高级教程-实例-方法:08. Java 实例 – break 关键字用法 1.返回顶部 1. Java 实例 - break 关键字用法  Java 实例 Ja ...