这几天在做动画的时候,遇到了一个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. ios导航栏和tabbar的坑

    多年不写ios,目前重构项目,发现navBar和tabbar需要注意的点,记录备忘 translucent属性会导致view起始点的变化,默认为透明,和设计图有色差,改成不透明以后,坐标位置有变化,修 ...

  2. AM二次开发中选择指定范围内的对象

    使用Spatial可以快速选择指定范围内的对象 例如下面的代码可以选择所有在[0,0,0]-[10m,10m,10m]这个盒子之内的对象: 其中ElementsInBox还可以指定对象类型做进一步筛选 ...

  3. mysql 事务学习

    1.事务 逻辑上的一组操作,组成这组操作的各个逻辑单元要么一起成功,要么一起失败. 2.事务特性 原子性:强调事务的不可分割.一致性:强调的是事务的执行的前后,数据的完整性要保持一致.隔离性:一个事务 ...

  4. 用ADB打开MUMU模拟器的WLAN用于设置代理IP

    adb connect 127.0.0.1:7555 adb shell am start -a android.intent.action.MAIN -n com.android.settings/ ...

  5. GCViewer / MAT

    jvm出现问题时,我们可以开启jmx功能,使用jvisualvm或者jconsole等监控其他机器上的jvm的运行情况,如https://www.cnblogs.com/princessd8251/p ...

  6. [UE4]Set Array Elem

    用一个新元素替换数组中已有的元素. “Size to Fit”:true,表示如果index参数大于数组所有,将会自动扩展数组大小.

  7. 第11章 拾遗4:IPv6(1)_报文格式和地址类型

    1. IPv4和IPv6协议栈的比较 (1)IPv6取代IPv4,支持IPv6的动态路由协议都属于IPv6协议(如RIPng.OSPFv3). (2)Internet控制消息协议IPv6版(ICMPv ...

  8. 第9章 应用层(5)_文件传输协议FTP

    6. 文件传输协议FTP 6.1 FTP主动和被动模式 (1)FTP协议 ①与其他协议不同,FTP协议在客户端访问FTP服务器时需要建立两个TCP连接.一个用来传输FTP命令,一个用来传输数据. ②在 ...

  9. Centos下安装Docker集群管理工具Shipyard

    一. Docker Shipyard是什么 ? shipyard是一个开源的docker管理平台,其特性主要包括: 支持镜像管理.容器管理. 支持控制台命令 容器资源消耗监控 支持集群swarm,可以 ...

  10. 安装memcache集群管理工具

    安装memcache集群管理工具magent 一.安装libevent tar xf libevent--stable.tar.gz cd libevent- ./configure --prefix ...