在Android中引起帧中的帧animationOutOfMemoryError
我有很多图像作为我的资源/可绘制文件夹中的帧(比方说约200)。 而使用这个图像,我想运行一个animation。 最长的animation是80帧。 我成功地能够点击一些点击button来运行animation,但是对于一些animation,它给了我OutOfMemoryError,说VM不能提供这样的内存。 它超出了VM预算。 我计算了所有图像的大小约10MB。 每个图像的大小是320×480像素。
我尝试使用Google,发现我需要使用System.gc()方法显式调用垃圾收集器。 我已经做到了,但仍然有一些内存错误。 任何人都可以请帮助我在这个。
一些代码: –
ImageView img = (ImageView)findViewById(R.id.xxx); img.setBackgroundResource(R.anim.angry_tail_animation); AnimationDrawable mailAnimation = (AnimationDrawable) img.getBackground(); MediaPlayer player = MediaPlayer.create(this.getApplicationContext(), R.raw.angry); if(mailAnimation.isRunning()) { mailAnimation.stop(); mailAnimation.start(); if (player.isPlaying()) { player.stop(); player.start(); } else { player.start(); } } else { mailAnimation.start(); if (player.isPlaying()) { player.stop(); player.start(); } else { player.start(); } }
这是我点击一个button写的代码…..
res / drawable / anim中的资源文件
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true" > <item android:drawable="@drawable/cat_angry0000" android:duration="50"/> <item android:drawable="@drawable/cat_angry0001" android:duration="50"/> <item android:drawable="@drawable/cat_angry0002" android:duration="50"/> <item android:drawable="@drawable/cat_angry0003" android:duration="50"/> <item android:drawable="@drawable/cat_angry0004" android:duration="50"/> <item android:drawable="@drawable/cat_angry0005" android:duration="50"/> <item android:drawable="@drawable/cat_angry0006" android:duration="50"/> <item android:drawable="@drawable/cat_angry0007" android:duration="50"/> <item android:drawable="@drawable/cat_angry0008" android:duration="50"/> <item android:drawable="@drawable/cat_angry0009" android:duration="50"/> <item android:drawable="@drawable/cat_angry0010" android:duration="50"/> <item android:drawable="@drawable/cat_angry0011" android:duration="50"/> <item android:drawable="@drawable/cat_angry0012" android:duration="50"/> <item android:drawable="@drawable/cat_angry0013" android:duration="50"/> <item android:drawable="@drawable/cat_angry0014" android:duration="50"/> <item android:drawable="@drawable/cat_angry0015" android:duration="50"/> <item android:drawable="@drawable/cat_angry0016" android:duration="50"/> <item android:drawable="@drawable/cat_angry0017" android:duration="50"/> <item android:drawable="@drawable/cat_angry0018" android:duration="50"/> <item android:drawable="@drawable/cat_angry0019" android:duration="50"/> <item android:drawable="@drawable/cat_angry0020" android:duration="50"/> <item android:drawable="@drawable/cat_angry0021" android:duration="50"/> <item android:drawable="@drawable/cat_angry0022" android:duration="50"/> <item android:drawable="@drawable/cat_angry0023" android:duration="50"/> <item android:drawable="@drawable/cat_angry0024" android:duration="50"/> <item android:drawable="@drawable/cat_angry0025" android:duration="50"/> </animation-list>
**以上是在setBackgroundResource中使用的资源文件,同样的方式我有另外10个文件用于其他不同的animation。 **
错误日志
01-16 22:23:41.594: E/AndroidRuntime(399): FATAL EXCEPTION: main 01-16 22:23:41.594: E/AndroidRuntime(399): java.lang.IllegalStateException: Could not execute method of the activity 01-16 22:23:41.594: E/AndroidRuntime(399): at android.view.View$1.onClick(View.java:2144) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.view.View.performClick(View.java:2485) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.view.View$PerformClick.run(View.java:9080) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.os.Handler.handleCallback(Handler.java:587) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.os.Handler.dispatchMessage(Handler.java:92) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.os.Looper.loop(Looper.java:123) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.app.ActivityThread.main(ActivityThread.java:3683) 01-16 22:23:41.594: E/AndroidRuntime(399): at java.lang.reflect.Method.invokeNative(Native Method) 01-16 22:23:41.594: E/AndroidRuntime(399): at java.lang.reflect.Method.invoke(Method.java:507) 01-16 22:23:41.594: E/AndroidRuntime(399): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 01-16 22:23:41.594: E/AndroidRuntime(399): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 01-16 22:23:41.594: E/AndroidRuntime(399): at dalvik.system.NativeStart.main(Native Method) 01-16 22:23:41.594: E/AndroidRuntime(399): Caused by: java.lang.reflect.InvocationTargetException 01-16 22:23:41.594: E/AndroidRuntime(399): at java.lang.reflect.Method.invokeNative(Native Method) 01-16 22:23:41.594: E/AndroidRuntime(399): at java.lang.reflect.Method.invoke(Method.java:507) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.view.View$1.onClick(View.java:2139) 01-16 22:23:41.594: E/AndroidRuntime(399): ... 11 more 01-16 22:23:41.594: E/AndroidRuntime(399): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget 01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:460) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:336) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.content.res.Resources.loadDrawable(Resources.java:1709) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.content.res.Resources.getDrawable(Resources.java:581) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.drawable.AnimationDrawable.inflate(AnimationDrawable.java:267) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:787) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.drawable.Drawable.createFromXml(Drawable.java:728) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.content.res.Resources.loadDrawable(Resources.java:1694) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.content.res.Resources.getDrawable(Resources.java:581) 01-16 22:23:41.594: E/AndroidRuntime(399): at android.view.View.setBackgroundResource(View.java:7533) 01-16 22:23:41.594: E/AndroidRuntime(399): at talking.cat.CatActivity.middleButtonClicked(CatActivity.java:83)
同样的方式,我有不同的button不同的animation…谢谢
我假设你的animation帧图像被压缩(PNG或JPG)。 压缩的大小对于计算显示它们需要多less内存是没有用的。 为此,您需要考虑未压缩的大小。 这将是像素数(320×480)乘以每个像素的字节数,通常是4(32位)。 那么对于你的图片,每一个将是614400字节。 对于您提供的26帧animation示例,将需要总共15,974,400字节来保存所有帧的原始位图数据,而不包括对象开销。
查看AnimationDrawable
的源代码,它似乎一次加载所有的帧到内存中,这基本上要做好性能。
你是否可以分配这么多的内存是非常依赖系统的。 我至less会build议在真实的设备上而不是模拟器上尝试。 您也可以尝试调整模拟器的可用RAM大小,但这只是猜测。
有很多方法可以使用BitmapFactory.inPreferredConfig
以更高效的格式(如RGB 565(而不是ARGB 8888))加载位图。 这样可以节省一些空间,但仍然可能不够。
如果你不能一次分配那么多的内存,你必须考虑其他的select。 大多数高性能graphics应用程序(例如游戏)从小graphics(精灵)或2D或3D基元(矩形,三angular形)的组合中绘制graphics。 为每个帧绘制全屏位图与渲染video效果相同; 不一定是最有效的。
你的animation的全部内容是否随着每一帧而改变? 另一个优化可能是animation只有实际改变的部分,并砍掉你的位图来说明这一点。
总而言之,您需要find一种使用较less内存来绘制animation的方法。 有很多select,但是这取决于你的animation需要看的很多。
我有同样的问题。 Android会一次加载所有可绘制对象,因此具有多个框架的animation会导致此错误。
我结束了创build我自己的简单的序列animation:
public class AnimationsContainer { public int FPS = 30; // animation FPS // single instance procedures private static AnimationsContainer mInstance; private AnimationsContainer() { }; public static AnimationsContainer getInstance() { if (mInstance == null) mInstance = new AnimationsContainer(); return mInstance; } // animation progress dialog frames private int[] mProgressAnimFrames = { R.drawable.logo_30001, R.drawable.logo_30002, R.drawable.logo_30003 }; // animation splash screen frames private int[] mSplashAnimFrames = { R.drawable.logo_ding200480001, R.drawable.logo_ding200480002 }; /** * @param imageView * @return progress dialog animation */ public FramesSequenceAnimation createProgressDialogAnim(ImageView imageView) { return new FramesSequenceAnimation(imageView, mProgressAnimFrames); } /** * @param imageView * @return splash screen animation */ public FramesSequenceAnimation createSplashAnim(ImageView imageView) { return new FramesSequenceAnimation(imageView, mSplashAnimFrames); } /** * AnimationPlayer. Plays animation frames sequence in loop */ public class FramesSequenceAnimation { private int[] mFrames; // animation frames private int mIndex; // current frame private boolean mShouldRun; // true if the animation should continue running. Used to stop the animation private boolean mIsRunning; // true if the animation currently running. prevents starting the animation twice private SoftReference<ImageView> mSoftReferenceImageView; // Used to prevent holding ImageView when it should be dead. private Handler mHandler; private int mDelayMillis; private OnAnimationStoppedListener mOnAnimationStoppedListener; private Bitmap mBitmap = null; private BitmapFactory.Options mBitmapOptions; public FramesSequenceAnimation(ImageView imageView, int[] frames, int fps) { mHandler = new Handler(); mFrames = frames; mIndex = -1; mSoftReferenceImageView = new SoftReference<ImageView>(imageView); mShouldRun = false; mIsRunning = false; mDelayMillis = 1000 / fps; imageView.setImageResource(mFrames[0]); // use in place bitmap to save GC work (when animation images are the same size & type) if (Build.VERSION.SDK_INT >= 11) { Bitmap bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap(); int width = bmp.getWidth(); int height = bmp.getHeight(); Bitmap.Config config = bmp.getConfig(); mBitmap = Bitmap.createBitmap(width, height, config); mBitmapOptions = new BitmapFactory.Options(); // setup bitmap reuse options. mBitmapOptions.inBitmap = mBitmap; mBitmapOptions.inMutable = true; mBitmapOptions.inSampleSize = 1; } } private int getNext() { mIndex++; if (mIndex >= mFrames.length) mIndex = 0; return mFrames[mIndex]; } /** * Starts the animation */ public synchronized void start() { mShouldRun = true; if (mIsRunning) return; Runnable runnable = new Runnable() { @Override public void run() { ImageView imageView = mSoftReferenceImageView.get(); if (!mShouldRun || imageView == null) { mIsRunning = false; if (mOnAnimationStoppedListener != null) { mOnAnimationStoppedListener.AnimationStopped(); } return; } mIsRunning = true; mHandler.postDelayed(this, mDelayMillis); if (imageView.isShown()) { int imageRes = getNext(); if (mBitmap != null) { // so Build.VERSION.SDK_INT >= 11 Bitmap bitmap = null; try { bitmap = BitmapFactory.decodeResource(imageView.getResources(), imageRes, mBitmapOptions); } catch (Exception e) { e.printStackTrace(); } if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { imageView.setImageResource(imageRes); mBitmap.recycle(); mBitmap = null; } } else { imageView.setImageResource(imageRes); } } } }; mHandler.post(runnable); } /** * Stops the animation */ public synchronized void stop() { mShouldRun = false; } } }
用法:
FramesSequenceAnimation anim = AnimationsContainer.getInstance().createSplashAnim(mSplashImageView); anim.start();
- 不要忘记阻止它…
我花了很多时间在这个,有两个不同的解决scheme,都好..
首先,问题是:1)Android以未压缩的位图格式将所有图像加载到RAM中。 2)Android使用资源缩放,所以在具有xxxhdpi显示的电话(如LG G3)上,每个帧占用一个TON空间,所以很快就会耗尽RAM。
解决scheme#1
1)旁观Android的资源扩展。 2)存储所有文件在内存中的字节数(这些很小,尤其是JPEG)。 3)逐帧生成位图,所以几乎不可能用完RAM。
缺点:当Android为新的Bitmaps分配内存并回收旧的日志时,它会监视日志。 它也在旧设备(Galaxy S1)上performance糟糕,但是在当前的预算手机上performance很好(阅读:我在Best Buy购买的价格为10美元的Alcatel C1)。 以下第二种解决scheme在较旧的设备上执行得更好,但在某些情况下可能仍会耗尽内存。
public class MyAnimationDrawable { public static class MyFrame { byte[] bytes; int duration; Drawable drawable; boolean isReady = false; } public interface OnDrawableLoadedListener { public void onDrawableLoaded(List<MyFrame> myFrames); } public static void loadRaw(final int resourceId, final Context context, final OnDrawableLoadedListener onDrawableLoadedListener) { loadFromXml(resourceId, context, onDrawableLoadedListener); } private static void loadFromXml(final int resourceId, final Context context, final OnDrawableLoadedListener onDrawableLoadedListener) { new Thread(new Runnable() { @Override public void run() { final ArrayList<MyFrame> myFrames = new ArrayList<>(); 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(); } } catch (IOException | XmlPullParserException e) { e.printStackTrace(); } // Run on UI Thread new Handler(context.getMainLooper()).post(new Runnable() { @Override public void run() { if (onDrawableLoadedListener != null) { onDrawableLoadedListener.onDrawableLoaded(myFrames); } } }); } }).run(); } public static void animateRawManually(int resourceId, final ImageView imageView, final Runnable onStart, final Runnable onComplete) { loadRaw(resourceId, imageView.getContext(), new OnDrawableLoadedListener() { @Override public void onDrawableLoaded(List<MyFrame> myFrames) { if (onStart != null) { onStart.run(); } animateRawManually(myFrames, imageView, onComplete); } }); } public static void animateRawManually(List<MyFrame> myFrames, ImageView imageView, Runnable onComplete) { animateRawManually(myFrames, imageView, onComplete, 0); } private static void animateRawManually(final List<MyFrame> myFrames, final ImageView imageView, final Runnable onComplete, final int frameNumber) { 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, onComplete, frameNumber + 1); } else { nextFrame.isReady = true; } } else { if (onComplete != null) { onComplete.run(); } } } } }, 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, onComplete, frameNumber + 1); } else { nextFrame.isReady = true; } } }).run(); } } }
**解决scheme#2 **
它加载XML资源,parsing并加载原始资源 – 从而绕过Android的资源扩展(这是大多数OutOfMemoryExceptions的负责),并创build一个AnimationDrawable。
优点:在旧设备(如Galaxy S1)
缺点:仍然可以耗尽内存,因为它将内存中的所有未压缩位图保存起来(但是它们更小,因为它们不像Android通常缩放图像那样缩放)
public static void animateManuallyFromRawResource(int animationDrawableResourceId, ImageView imageView, Runnable onStart, Runnable onComplete) { AnimationDrawable animationDrawable = new AnimationDrawable(); XmlResourceParser parser = imageView.getContext().getResources().getXml(animationDrawableResourceId); 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")) { Drawable drawable = 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)); byte[] bytes = IoUtils.readBytes(imageView.getContext().getResources().openRawResource(resId)); drawable = new BitmapDrawable(imageView.getContext().getResources(), BitmapFactory.decodeByteArray(bytes, 0, bytes.length)); } else if (parser.getAttributeName(i).equals("duration")) { duration = parser.getAttributeIntValue(i, 66); } } animationDrawable.addFrame(drawable, duration); } } else if (eventType == XmlPullParser.END_TAG) { } else if (eventType == XmlPullParser.TEXT) { } eventType = parser.next(); } } catch (IOException | XmlPullParserException e) { e.printStackTrace(); } if (onStart != null) { onStart.run(); } animateDrawableManually(animationDrawable, imageView, onComplete, 0); } private static void animateDrawableManually(final AnimationDrawable animationDrawable, final ImageView imageView, final Runnable onComplete, final int frameNumber) { final Drawable frame = animationDrawable.getFrame(frameNumber); imageView.setImageDrawable(frame); 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() == frame) { if (frameNumber + 1 < animationDrawable.getNumberOfFrames()) { // Animate next frame animateDrawableManually(animationDrawable, imageView, onComplete, frameNumber + 1); } else { // Animation complete if (onComplete != null) { onComplete.run(); } } } } }, animationDrawable.getDuration(frameNumber)); }
如果仍然有内存问题,请使用较小的图像…或者存储资源名称+持续时间,并在每个帧上生成字节数组+ Drawable。 这几乎肯定会造成帧之间太多的斩波,但使用几乎为零的RAM。
我创build了一个animation类,根据传入的drawables资源和帧持续时间来显示帧。
protected class SceneAnimation{ private ImageView mImageView; private int[] mFrameRess; private int[] mDurations; private int mDuration; private int mLastFrameNo; private long mBreakDelay; public SceneAnimation(ImageView pImageView, int[] pFrameRess, int[] pDurations){ mImageView = pImageView; mFrameRess = pFrameRess; mDurations = pDurations; mLastFrameNo = pFrameRess.length - 1; mImageView.setImageResource(mFrameRess[0]); play(1); } public SceneAnimation(ImageView pImageView, int[] pFrameRess, int pDuration){ mImageView = pImageView; mFrameRess = pFrameRess; mDuration = pDuration; mLastFrameNo = pFrameRess.length - 1; mImageView.setImageResource(mFrameRess[0]); playConstant(1); } public SceneAnimation(ImageView pImageView, int[] pFrameRess, int pDuration, long pBreakDelay){ mImageView = pImageView; mFrameRess = pFrameRess; mDuration = pDuration; mLastFrameNo = pFrameRess.length - 1; mBreakDelay = pBreakDelay; mImageView.setImageResource(mFrameRess[0]); playConstant(1); } private void play(final int pFrameNo){ mImageView.postDelayed(new Runnable(){ public void run() { mImageView.setImageResource(mFrameRess[pFrameNo]); if(pFrameNo == mLastFrameNo) play(0); else play(pFrameNo + 1); } }, mDurations[pFrameNo]); } private void playConstant(final int pFrameNo){ mImageView.postDelayed(new Runnable(){ public void run() { mImageView.setImageResource(mFrameRess[pFrameNo]); if(pFrameNo == mLastFrameNo) playConstant(0); else playConstant(pFrameNo + 1); } }, pFrameNo==mLastFrameNo && mBreakDelay>0 ? mBreakDelay : mDuration); } };
它是这样使用的:
private ImageView mTapScreenTextAnimImgView; private final int[] mTapScreenTextAnimRes = {R.drawable.tap0001_b, R.drawable.tap0002_b, R.drawable.tap0003_b, R.drawable.tap0004_b, R.drawable.tap0005_b, R.drawable.tap0006_b, R.drawable.tap0005_b, R.drawable.tap0004_b, R.drawable.tap0003_b, R.drawable.tap0002_b, R.drawable.tap0001_b}; private final int mTapScreenTextAnimDuration = 100; private final int mTapScreenTextAnimBreak = 500;
并在onCreate:
mTapScreenTextAnimImgView = (ImageView) findViewById(R.id.scene1AnimBottom); new SceneAnimation(mTapScreenTextAnimImgView, mTapScreenTextAnimRes, mTapScreenTextAnimDuration, mTapScreenTextAnimBreak);
我遇到了这个问题,通过做以下两件事来解决它:
- 将animation图像的分辨率降低一半,为未压缩字节的1/4。
- 把图像放在drawable-nodpi文件夹中,这样它们就不会被Android放大。
在执行步骤1之后,我的animation仍然无法加载到某些电话上。步骤2在这些电话上工作。
希望这能节省一些人的时间。
编辑:我仍然遇到崩溃之后去播放AnimationDrawable的活动,但我现在工作。 以下是我所做的其他事情:
- 不要在xml中使用animation列表。 相反,每次需要使用时都要创buildAnimationDrawable。 否则,下次您从资源加载可绘制的animation时,它仍然会尝试使用您最终回收的位图。
- 当您完成使用时,请回收AnimationDrawable中的位图。 这是释放记忆的魔力。
- 使用Android设备监视器来监视堆中分配的字节。
这里是我用来创buildAnimationDrawable的代码:
protected AnimationDrawable CreateLoadingAnimationDrawable() { AnimationDrawable animation = new AnimationDrawable (); animation.OneShot = false; for (int i = 0; i < kNumberOfFrames; ++i) { int index = (i * 2) + 1; string stringIndex = index.ToString ("00"); string bitmapStringId = kBaseAnimationName + stringIndex; int resID = this.Resources.GetIdentifier (bitmapStringId, "drawable", this.PackageName); Bitmap bitmap = BitmapFactory.DecodeResource (this.Resources, resID); BitmapDrawable frame = new BitmapDrawable (bitmap); //Drawable frame = Resources.GetDrawable (resID); animation.AddFrame (frame, 111); } return animation; }
和代码释放位图,当你完成使用它们。 你可以在OnPause或OnDestroy中做到这一点。 _loadingAnimation是我上面创build的AnimationDrawable。 我很想知道在这种情况下,SetCallback()为你做了什么。 我只是从其他地方复制它。
if (_loadingAnimation != null) { _loadingAnimation.Stop (); _loadingImageView.SetBackgroundResource (Resource.Drawable.loading_anim_full7001); for (int i = 0; i < _loadingAnimation.NumberOfFrames; ++i) { BitmapDrawable frame = _loadingAnimation.GetFrame (i) as BitmapDrawable; if (frame != null) { Android.Graphics.Bitmap bitmap = frame.Bitmap; bitmap.Recycle (); frame.SetCallback(null); } } _loadingAnimation.SetCallback(null); _loadingAnimation = null; }
摊晒
这是sdk的大问题,但它可以通过使用线程并发加载位图图像,而不是同时加载整个图像来解决。
我解决了我的outOfMemoryError问题,通过严格的削减帧率,并缩小在gimp的图像。 根据你在做什么,你可能会比你想象的less得多的fps。
我已经解决了这个问题,把所有的图像放在数组中,并在显示每个图像后使用延迟。 图像数组源文件res / string <!-- Array table for the pictures to show on the spinner--> <array name="spinner_list"> <item>@drawable/arrows_loop__00000_org</item> <item>@drawable/arrows_loop__00005_org</item> <item >@drawable/arrows_loop__00010_org</item> <item>@drawable/arrows_loop__00015_org</item> <item >@drawable/arrows_loop__00020_org</item> <item >@drawable/arrows_loop__00025_org</item> . . . </array>
<!-- Array table for the pictures to show on the spinner--> <array name="spinner_list"> <item>@drawable/arrows_loop__00000_org</item> <item>@drawable/arrows_loop__00005_org</item> <item >@drawable/arrows_loop__00010_org</item> <item>@drawable/arrows_loop__00015_org</item> <item >@drawable/arrows_loop__00020_org</item> <item >@drawable/arrows_loop__00025_org</item> . . . </array>
<!-- Array table for the pictures to show on the spinner--> <array name="spinner_list"> <item>@drawable/arrows_loop__00000_org</item> <item>@drawable/arrows_loop__00005_org</item> <item >@drawable/arrows_loop__00010_org</item> <item>@drawable/arrows_loop__00015_org</item> <item >@drawable/arrows_loop__00020_org</item> <item >@drawable/arrows_loop__00025_org</item> . . . </array>
我声明了微调器imageView private static ImageView imagespinner;
然后在我的课堂上,我在这里称之为:
final TypedArray imgs = getResources().obtainTypedArray(R.array.spinner_list); runimage(imgs, imgs.length());
然后在runimage我做这样的延迟循环:
/* handle the spinner frame by frame */
公共无效的runimage(最终TypedArray数组,int索引){
int size = array.length(); if(index<size) {// show in sequence the images final int localindex= index; handler.postDelayed(new Runnable() { public void run() { imagespinner.setImageResource(array.getResourceId(localindex, -1));// find the picture to show runimage(array,(localindex+1));// because use final arg need to do the increase inside } }, 55); } else // after show all images go ahead { textview2.setVisibility(View.VISIBLE); handler.postDelayed(myRunnablewait, 2000); // make some time to see text before go to ather fragment } }
所以我在imagespinner上以55milsec的延迟运行所有图像。 完成后做好工作。
类似于其他的答案,使用rxjava:
public final class RxSequenceAnimation { private static final int[] PNG_RESOURCES = new int[]{ R.drawable.sequence_frame_00, R.drawable.sequence_frame_01, R.drawable.sequence_frame_02 }; private static final String TAG = "rx-seq-anim"; private final Resources mResource; private final ImageView mImageView; private final byte[][] RAW_PNG_DATA = new byte[PNG_RESOURCES.length][]; private final byte[] buff = new byte[1024]; private Subscription sub; public RxSequenceAnimation(Resources resources, ImageView imageView) { mResource = resources; mImageView = imageView; } public void start() { sub = Observable .interval(16, TimeUnit.MILLISECONDS) .map(new Func1<Long, Bitmap>() { @Override public Bitmap call(Long l) { int i = (int) (l % PNG_RESOURCES.length); if (RAW_PNG_DATA[i] == null) { // read raw png data (compressed) if not read already into RAM try { RAW_PNG_DATA[i] = read(PNG_RESOURCES[i]); } catch (IOException e) { Log.e(TAG, "IOException " + String.valueOf(e)); } Log.d(TAG, "decoded " + i + " size " + RAW_PNG_DATA[i].length); } // decode directly from RAM - only one full blown bitmap is in RAM at a time return BitmapFactory.decodeByteArray(RAW_PNG_DATA[i], 0, RAW_PNG_DATA[i].length); } }) .subscribeOn(Schedulers.newThread()) .onBackpressureDrop() .observeOn(AndroidSchedulers.mainThread()) .doOnNext(new Action1<Bitmap>() { @Override public void call(Bitmap b) { mImageView.setImageBitmap(b); } }) .subscribe(LogErrorSubscriber.newInstance(TAG)); } public void stop() { if (sub != null) { sub.unsubscribe(); } } private byte[] read(int resId) throws IOException { return streamToByteArray(inputStream(resId)); } private InputStream inputStream(int id) { return mResource.openRawResource(id); } private byte[] streamToByteArray(InputStream is) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int i; while ((i = is.read(buff, 0, buff.length)) > 0) { baos.write(buff, 0, i); } byte[] bytes = baos.toByteArray(); is.close(); return bytes; } }