如何解决Android帧动画出现的内存溢出
这几天在做动画的时候,遇到了一个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帧动画出现的内存溢出的更多相关文章
- 解决android加载图片时内存溢出问题
尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过jav ...
- Android开发中如何解决加载大图片时内存溢出的问题
Android开发中如何解决加载大图片时内存溢出的问题 在Android开发过程中,我们经常会遇到加载的图片过大导致内存溢出的问题,其实类似这样的问题已经屡见不鲜了,下面将一些好的解决方案分享给 ...
- 图片_ _Android有效解决加载大图片时内存溢出的问题 2
Android有效解决加载大图片时内存溢出的问题 博客分类: Android Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或 setImageResource或 Bit ...
- android 帧动画的实现及图片过多时OOM解决方案(一)
一,animation_list.xml中静态配置帧动画的顺序,如下: <?xml version="1.0" encoding="utf-8"?> ...
- Android帧动画实现,防OOM,比原生动画集节约超过十倍的资源
2015年项目接到一个需求,实现一个向导动画,这个动画一共六十张图片,当时使用的是全志A33的开发(512的内存),通过使用Android的动画集实现,效果特别卡顿,然后想到这样的方式来实现,效果非常 ...
- android 帧动画,补间动画,属性动画的简单总结
帧动画——FrameAnimation 将一系列图片有序播放,形成动画的效果.其本质是一个Drawable,是一系列图片的集合,本身可以当做一个图片一样使用 在Drawable文件夹下,创建ani ...
- android 帧动画
首先在res/drawable/name1.xml/定义一组图片集合: <?xml version="1.0" encoding="utf-8"?> ...
- android帧动画,移动位置,缩放,改变透明度等动画讲解
1.苦逼的需求又来了,需要实现一些动画效果,第一个想到的是播放gif图片,但是这样会占包的资源,并且清晰度不高,于是想着程序实现,自己用帧动画+缩放+移动+透明度 实现了一些想要的效果,这里跟大家分享 ...
- Android帧动画笔记
创建drawable资源文件,选择animation-list<?xml version="1.0" encoding="utf-8"?><a ...
随机推荐
- modbus tcp 入门详解
Modbus tcp 格式说明 通讯机制 附C#测试工具用于学习,测试 前言: 之前的博客介绍了如何用C#来读写modbus tcp服务器的数据,文章:http://www.cnblogs.com ...
- [蓝桥杯]ALGO-48.算法训练_关联矩阵
题目描述: 问题描述 有一个n个结点m条边的有向图,请输出他的关联矩阵. 输入格式 第一行两个整数n.m,表示图中结点和边的数目.n<=,m<=. 接下来m行,每行两个整数a.b,表示图中 ...
- vagrant box保存路径修改
add box的时候默认保存在C盘用户文件夹 C:\Users\xxx.vagrant.d,通过设置VAGRANT_HOME环境变量改变默认位置 WIN setx VAGRANT_HOME “X:/y ...
- windows2008系统缓存修改
Windwos Server 2008 中经常有物理内存占用较高,但任务管理器中各进程的内存看起来占用并不多情况. 这可能是由于OS为保证服务器性能不限制系统缓存,服务器系统长时间执行I/O,系统文件 ...
- Wireshark抓包工具--TCP数据包seq ack等解读
1.Wireshark的数据包详情窗口,如果是用中括号[]括起来的,表示注释,在数据包中不占字节 2.在二进制窗口中,如“DD 3D”,表示两个字节,一个字节8位 3.TCP数据包中,seq表示这个包 ...
- R语言—统计结果输出至本地文件方法总结
1.sink()在代码开始前加一行:sink(“output.txt”),就会自动把结果全部输出到工作文件夹下的output.txt文本文档.这时在R控制台的输出窗口中是看不到输出结果的.代码结束时用 ...
- HTTP 2.0 原理详细分析
HTTP 2.0是在SPDY(An experimental protocol for a faster web, The Chromium Projects)基础上形成的下一代互联网通信协议.HTT ...
- 构建Redis主从镜像
构建Redis的基础镜像,然后基于这个基础镜像构建主Redis镜像和从Redis镜像. 1.构建Redis基础镜像 创建redis基础镜像目录 [root@localhost mnt]# mkdir ...
- Centos7.0根分区扩容
添加一块10G的硬盘 查看磁盘大小 # df -Th 查看物理卷组 # vgdisplay 查看逻辑卷 # lvs 查看磁盘信息 # fdisk -l 对添加的10G硬盘分区 # fdisk /dev ...
- @postconstruct初始化的操作(转载)
原文地址:https://www.cnblogs.com/qingruihappy/p/7861623.html 从Java EE 5规范开始,Servlet中增加了两个影响Servlet生命周期的注 ...