Android 使用帧动画内存溢出解决方案
Android使用帧动画内存溢出解决方案
最近在项目遇到的动画效果不好实现,就让UI切成图,采用帧动画实现效果,但是在使用animation-list时,图片也就11张,每张图片大概560k左右,结果内存溢出,崩溃了,自己用了三张都崩溃;拿代码说;
1.anin_searh.xml
<?xmlversion="1.0"encoding="utf-8"?> <animation-listxmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true"> <itemandroid:drawable="@drawable/a1"android:duration="100"></item> <itemandroid:drawable="@drawable/a2"android:duration="100"></item> <itemandroid:drawable="@drawable/a4"android:duration="100"></item> <itemandroid:drawable="@drawable/a5"android:duration="100"></item> <itemandroid:drawable="@drawable/a6"android:duration="100"></item> <itemandroid:drawable="@drawable/a7"android:duration="100"></item> <itemandroid:drawable="@drawable/a8"android:duration="100"></item> <itemandroid:drawable="@drawable/a9"android:duration="100"></item> <itemandroid:drawable="@drawable/a10"android:duration="100"></item> <itemandroid:drawable="@drawable/a11"android:duration="100"></item> </animation-list>
2.使用帧动画
search_scale_iv.setBackgroundResource(R.drawable.anim_search); AnimationDrawabledrawable=(AnimationDrawable)search_scale_iv.getBackground(); drawable.start();
结果setBackgroundResource出现内存溢出,这个方法其实获取drawable时候,会消耗很多内存,很容易内存溢出,崩溃。
3.解决方法:在网上找了个类,处理,结果我使用11张560k大小图片,没有内存溢出;
importandroid.content.Context;
importandroid.content.res.XmlResourceParser;
importandroid.graphics.BitmapFactory;
importandroid.graphics.drawable.AnimationDrawable;
importandroid.graphics.drawable.BitmapDrawable;
importandroid.graphics.drawable.Drawable;
importandroid.os.Handler;
importandroid.widget.ImageView;
importorg.apache.commons.io.IOUtils;
importorg.xmlpull.v1.XmlPullParser;
importorg.xmlpull.v1.XmlPullParserException;
importjava.io.IOException;
importjava.util.ArrayList;
importjava.util.List;
/****
*此工具类源于stackoverflow
*原文链接: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字节数组
********/
publicclassMyAnimationDrawable{
publicstaticclassMyFrame{
byte[]bytes;
intduration;
Drawabledrawable;
booleanisReady=false;
}
publicinterfaceOnDrawableLoadedListener{
publicvoidonDrawableLoaded(List<MyFrame>myFrames);
}
//1
/***
*性能更优
*在animation-list中设置时间
***/
publicstaticvoidanimateRawManuallyFromXML(intresourceId,
finalImageViewimageView,finalRunnableonStart,
finalRunnableonComplete){
loadRaw(resourceId,imageView.getContext(),
newOnDrawableLoadedListener(){
@Override
publicvoidonDrawableLoaded(List<MyFrame>myFrames){
if(onStart!=null){
onStart.run();
}
animateRawManually(myFrames,imageView,onComplete);
}
});
}
//2
privatestaticvoidloadRaw(finalintresourceId,finalContextcontext,
finalOnDrawableLoadedListeneronDrawableLoadedListener){
loadFromXml(resourceId,context,onDrawableLoadedListener);
}
//3
privatestaticvoidloadFromXml(finalintresourceId,
finalContextcontext,
finalOnDrawableLoadedListeneronDrawableLoadedListener){
newThread(newRunnable(){
@Override
publicvoidrun(){
finalArrayList<MyFrame>myFrames=newArrayList<MyFrame>();
XmlResourceParserparser=context.getResources().getXml(
resourceId);
try{
inteventType=parser.getEventType();
while(eventType!=XmlPullParser.END_DOCUMENT){
if(eventType==XmlPullParser.START_DOCUMENT){
}elseif(eventType==XmlPullParser.START_TAG){
if(parser.getName().equals("item")){
byte[]bytes=null;
intduration=1000;
for(inti=0;i<parser.getAttributeCount();i++){
if(parser.getAttributeName(i).equals(
"drawable")){
intresId=Integer.parseInt(parser
.getAttributeValue(i)
.substring(1));
bytes=IOUtils.toByteArray(context
.getResources()
.openRawResource(resId));
}elseif(parser.getAttributeName(i)
.equals("duration")){
duration=parser.getAttributeIntValue(
i,1000);
}
}
MyFramemyFrame=newMyFrame();
myFrame.bytes=bytes;
myFrame.duration=duration;
myFrames.add(myFrame);
}
}elseif(eventType==XmlPullParser.END_TAG){
}elseif(eventType==XmlPullParser.TEXT){
}
eventType=parser.next();
}
}catch(IOExceptione){
e.printStackTrace();
}catch(XmlPullParserExceptione2){
//TODO:handleexception
e2.printStackTrace();
}
//RunonUIThread
newHandler(context.getMainLooper()).post(newRunnable(){
@Override
publicvoidrun(){
if(onDrawableLoadedListener!=null){
onDrawableLoadedListener.onDrawableLoaded(myFrames);
}
}
});
}
}).run();
}
//4
privatestaticvoidanimateRawManually(List<MyFrame>myFrames,
ImageViewimageView,RunnableonComplete){
animateRawManually(myFrames,imageView,onComplete,0);
}
//5
privatestaticvoidanimateRawManually(finalList<MyFrame>myFrames,
finalImageViewimageView,finalRunnableonComplete,
finalintframeNumber){
finalMyFramethisFrame=myFrames.get(frameNumber);
if(frameNumber==0){
thisFrame.drawable=newBitmapDrawable(imageView.getContext()
.getResources(),BitmapFactory.decodeByteArray(
thisFrame.bytes,0,thisFrame.bytes.length));
}else{
MyFramepreviousFrame=myFrames.get(frameNumber-1);
((BitmapDrawable)previousFrame.drawable).getBitmap().recycle();
previousFrame.drawable=null;
previousFrame.isReady=false;
}
imageView.setImageDrawable(thisFrame.drawable);
newHandler().postDelayed(newRunnable(){
@Override
publicvoidrun(){
//MakesureImageViewhasn'tbeenchangedtoadifferentImage
//inthistime
if(imageView.getDrawable()==thisFrame.drawable){
if(frameNumber+1<myFrames.size()){
MyFramenextFrame=myFrames.get(frameNumber+1);
if(nextFrame.isReady){
//Animatenextframe
animateRawManually(myFrames,imageView,onComplete,
frameNumber+1);
}else{
nextFrame.isReady=true;
}
}else{
if(onComplete!=null){
onComplete.run();
}
}
}
}
},thisFrame.duration);
//Loadnextframe
if(frameNumber+1<myFrames.size()){
newThread(newRunnable(){
@Override
publicvoidrun(){
MyFramenextFrame=myFrames.get(frameNumber+1);
nextFrame.drawable=newBitmapDrawable(imageView
.getContext().getResources(),
BitmapFactory.decodeByteArray(nextFrame.bytes,0,
nextFrame.bytes.length));
if(nextFrame.isReady){
//Animatenextframe
animateRawManually(myFrames,imageView,onComplete,
frameNumber+1);
}else{
nextFrame.isReady=true;
}
}
}).run();
}
}
//第二种方法
/***
*代码中控制时间,但不精确
*duration=1000;
*****/
publicstaticvoidanimateManuallyFromRawResource(
intanimationDrawableResourceId,ImageViewimageView,
RunnableonStart,RunnableonComplete,intduration)throwsIOException,
XmlPullParserException{
AnimationDrawableanimationDrawable=newAnimationDrawable();
XmlResourceParserparser=imageView.getContext().getResources()
.getXml(animationDrawableResourceId);
inteventType=parser.getEventType();
while(eventType!=XmlPullParser.END_DOCUMENT){
if(eventType==XmlPullParser.START_DOCUMENT){
}elseif(eventType==XmlPullParser.START_TAG){
if(parser.getName().equals("item")){
Drawabledrawable=null;
for(inti=0;i<parser.getAttributeCount();i++){
if(parser.getAttributeName(i).equals("drawable")){
intresId=Integer.parseInt(parser
.getAttributeValue(i).substring(1));
byte[]bytes=IOUtils.toByteArray(imageView
.getContext().getResources()
.openRawResource(resId));//IOUtils.readBytes
drawable=newBitmapDrawable(imageView
.getContext().getResources(),
BitmapFactory.decodeByteArray(bytes,0,
bytes.length));
}elseif(parser.getAttributeName(i)
.equals("duration")){
duration=parser.getAttributeIntValue(i,66);
}
}
animationDrawable.addFrame(drawable,duration);
}
}elseif(eventType==XmlPullParser.END_TAG){
}elseif(eventType==XmlPullParser.TEXT){
}
eventType=parser.next();
}
if(onStart!=null){
onStart.run();
}
animateDrawableManually(animationDrawable,imageView,onComplete,0);
}
privatestaticvoidanimateDrawableManually(
finalAnimationDrawableanimationDrawable,
finalImageViewimageView,finalRunnableonComplete,
finalintframeNumber){
finalDrawableframe=animationDrawable.getFrame(frameNumber);
imageView.setImageDrawable(frame);
newHandler().postDelayed(newRunnable(){
@Override
publicvoidrun(){
//MakesureImageViewhasn'tbeenchangedtoadifferentImage
//inthistime
if(imageView.getDrawable()==frame){
if(frameNumber+1<animationDrawable.getNumberOfFrames()){
//Animatenextframe
animateDrawableManually(animationDrawable,imageView,
onComplete,frameNumber+1);
}else{
//Animationcomplete
if(onComplete!=null){
onComplete.run();
}
}
}
}
},animationDrawable.getDuration(frameNumber));
}
}
这里需要导入jar,
importorg.apache.commons.io.IOUtils;
4.然后通过上述类,来调用自己的动画xml,
MyAnimationDrawable.animateRawManuallyFromXML(R.drawable.anim_search,
search_scale_iv,newRunnable(){
@Override
publicvoidrun(){
//TODOonStart
//动画开始时回调
log.d("","start");
}
},newRunnable(){
@Override
publicvoidrun(){
//TODOonComplete
//动画结束时回调
log.d("","end");
}
});
这样在使用帧动画时,可以有效的适度防止内存溢出,谁还有什么办法,欢迎交流!
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!