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"); } });
这样在使用帧动画时,可以有效的适度防止内存溢出,谁还有什么办法,欢迎交流!
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!