最近工作中使用了帧动画,就是基础的使用运行也没问题,上线了发现好多OOM问题。上网搜了下解决方法,一大堆。我是参考这个
https://www.cnblogs.com/mcfawa/p/5192335.html
,又做了一些修改,原有的是一个动画序列帧执行完就结束了,但我的要求是动画需要一只重复执行,直到我让它停止。因此对其做了修改。你要不需要我的这种需求可以参考上边的链接。
第一步、创建一个自定义类:
/**
* Created by zyt on 2018/1/2. 解决帧动画产生的OOM
* 优化:实现动画一直运行,添加了停止动画方法
* <p>
* 此工具类源于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 FrameAnimationDrawable {
private static FrameAnimationDrawable fDrawable;
private boolean isStop = false;//是否停止动画
private int frameNumber;//当前执行第几帧动画
/**
* 单例模式创建此类
*
* @return
*/
public static FrameAnimationDrawable create() {
if (fDrawable == null) {
synchronized (FrameAnimationDrawable.class) {
if (fDrawable == null) {
fDrawable = new FrameAnimationDrawable();
}
}
}
return fDrawable;
}
public static class MyFrame {
byte[] bytes;
int duration;
Drawable drawable;
boolean isReady = false;
}
/***
* 性能更优
* 在animation-list中设置时间
* **/
public void animateRawManuallyFromXML(int resourceId, ImageView imageView) {
isStop = false;
loadFromXml(resourceId, imageView);
}
/**
* XmlPullParser 解析
*
* @param resourceId
* @param imageView
*/
//2
private void loadFromXml(final int resourceId, final ImageView imageView) {
final Context context = imageView.getContext();
//XmlPullParser 解析
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();
}
//解析完成执行动画
animateRawManually(myFrames, imageView);
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e2) {
e2.printStackTrace();
}
}
}).run();
}
// 3
private void animateRawManually(List<MyFrame> myFrames, ImageView imageView) {
animateRawManually(myFrames, imageView, 0);
}
// 4
private void animateRawManually(final List<MyFrame> myFrames,
final ImageView imageView, final int currentFrameNumber) {
if (isStop) {//结束动画
imageView.clearAnimation();//清除动画
System.gc();//提醒系统回收之前的
return;
}
frameNumber = currentFrameNumber;
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, frameNumber + 1);
} else {
nextFrame.isReady = true;
}
} else {
//重新开始播放
frameNumber = -1;
animateRawManually(myFrames, imageView, frameNumber + 1);
}
}
}
}, 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, frameNumber + 1);
} else {
nextFrame.isReady = true;
}
}
}).run();
}
}
/**
* 停止动画
*
* @param isStop
*/
public void stopAnimation(boolean isStop) {
this.isStop = isStop;
}
}
第二步、在你需要的地方调用即可:
//执行动画
FrameAnimationDrawable.create().animateRawManuallyFromXML(R.drawable.tt_wave, testImageView);
//停止动画
FrameAnimationDrawable.create().stopAnimation(true);
都做了基本的注释,应该没什么问题,这里为了自己以后需要记录下,顺便方便下需要的同学,若那里写的有问题欢迎留言!