当我的动态壁纸超过 30 帧时出现内存不足错误
我一直在尝试用开机动画制作一个简单的动态壁纸。所以基本上我的可绘制文件夹中有大约 50 个 .png。我可以将动画设置为大约 10-20 帧,效果很好。但是一旦我将其设置为大约 30 帧...我就会收到 OutOfMemory 错误。我希望也许有人可以看一下我的代码,并举例说明如何实现更多帧?这会有很大帮助,我已经看了几个小时了> <
这是我的代码:
package com.androidnetwork.animlivewp;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Handler;
import android.os.SystemClock;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
public class AnimatedLiveWallpaper extends WallpaperService {
private final Handler mHandler = new Handler();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public Engine onCreateEngine() {
return new CubeEngine();
}
class CubeEngine extends Engine {
private final Paint mPaint = new Paint();
private float mPosY;
private boolean mAnime = true;
private Matrix mMatrix = new Matrix();
private final Runnable mDrawAnim = new Runnable() {
public void run() {
drawFrame();
}
};
private boolean mVisible;
private static final int NUM_RES = 30;
private final Bitmap[] mPics = new Bitmap[NUM_RES];
CubeEngine() {
Resources res = getResources();
for (int i = 0; i< NUM_RES; i++) {
int id = res.getIdentifier("boot_00" + (100 + (i + 1)), "drawable", "com.androidnetwork.animlivewp");
mPics[i] = BitmapFactory.decodeResource(res, id);
}
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
setTouchEventsEnabled(false);
}
@Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawAnim);
}
@Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
drawFrame();
} else {
mHandler.removeCallbacks(mDrawAnim);
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
float w = mPics[0].getWidth();
float h = mPics[0].getHeight();
float s = width / (float)w;
mMatrix.reset();
mMatrix.setScale(s, s);
mPosY = (height - (h * s)) / 2f;
drawFrame();
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(mDrawAnim);
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xStep, float yStep, int xPixels, int yPixels) {
drawFrame();
}
@Override
public void onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
mAnime = !mAnime;
}
super.onTouchEvent(event);
}
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// draw something
drawAnim(c);
//drawTouchPoint(c);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
// Reschedule the next redraw
mHandler.removeCallbacks(mDrawAnim);
if (mVisible && mAnime) {
mHandler.postDelayed(mDrawAnim, 1000 / 10);
}
}
private int idx = 0;
void drawAnim(Canvas c) {
c.save();
c.translate(0, mPosY);
c.drawBitmap(mPics[idx], mMatrix, mPaint);
if (mAnime) ++idx;
if (idx == NUM_RES) idx = 0;
c.restore();
}
}
}
如果有帮助的话,这是一个 logcat:
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): FATAL EXCEPTION: main
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.Bitmap.nativeCreate(Native Method)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.Bitmap.createBitmap(Bitmap.java:468)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.Bitmap.createBitmap(Bitmap.java:435)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:488)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:462)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:346)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:372)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at com.androidnetwork.animlivewp.AnimatedLiveWallpaper$CubeEngine.<init>(AnimatedLiveWallpaper.java:55)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at com.androidnetwork.animlivewp.AnimatedLiveWallpaper.onCreateEngine(AnimatedLiveWallpaper.java:32)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.service.wallpaper.WallpaperService$IWallpaperEngineWrapper.executeMessage(WallpaperService.java:814)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:61)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.os.Handler.dispatchMessage(Handler.java:99)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.os.Looper.loop(Looper.java:123)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.app.ActivityThread.main(ActivityThread.java:4627)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at java.lang.reflect.Method.invokeNative(Native Method)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at java.lang.reflect.Method.invoke(Method.java:521)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at dalvik.system.NativeStart.main(Native Method)
I've been trying to make a simple live wallpaper out of a boot animation. So basically i have about 50 .pngs in my drawable folder. I'm able to set the animation to about 10-20 frames and it works great. But once i set it to about 30 frames...I get an OutOfMemory Error. I was hoping maybe someone could take a look at my code and maybe give an example of how I could achieve more frames? That would help so much i've been looking at this for hours > <
Here's my code:
package com.androidnetwork.animlivewp;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Handler;
import android.os.SystemClock;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
public class AnimatedLiveWallpaper extends WallpaperService {
private final Handler mHandler = new Handler();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public Engine onCreateEngine() {
return new CubeEngine();
}
class CubeEngine extends Engine {
private final Paint mPaint = new Paint();
private float mPosY;
private boolean mAnime = true;
private Matrix mMatrix = new Matrix();
private final Runnable mDrawAnim = new Runnable() {
public void run() {
drawFrame();
}
};
private boolean mVisible;
private static final int NUM_RES = 30;
private final Bitmap[] mPics = new Bitmap[NUM_RES];
CubeEngine() {
Resources res = getResources();
for (int i = 0; i< NUM_RES; i++) {
int id = res.getIdentifier("boot_00" + (100 + (i + 1)), "drawable", "com.androidnetwork.animlivewp");
mPics[i] = BitmapFactory.decodeResource(res, id);
}
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
setTouchEventsEnabled(false);
}
@Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawAnim);
}
@Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
drawFrame();
} else {
mHandler.removeCallbacks(mDrawAnim);
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
float w = mPics[0].getWidth();
float h = mPics[0].getHeight();
float s = width / (float)w;
mMatrix.reset();
mMatrix.setScale(s, s);
mPosY = (height - (h * s)) / 2f;
drawFrame();
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(mDrawAnim);
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xStep, float yStep, int xPixels, int yPixels) {
drawFrame();
}
@Override
public void onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
mAnime = !mAnime;
}
super.onTouchEvent(event);
}
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// draw something
drawAnim(c);
//drawTouchPoint(c);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
// Reschedule the next redraw
mHandler.removeCallbacks(mDrawAnim);
if (mVisible && mAnime) {
mHandler.postDelayed(mDrawAnim, 1000 / 10);
}
}
private int idx = 0;
void drawAnim(Canvas c) {
c.save();
c.translate(0, mPosY);
c.drawBitmap(mPics[idx], mMatrix, mPaint);
if (mAnime) ++idx;
if (idx == NUM_RES) idx = 0;
c.restore();
}
}
}
And here's a logcat if that'll help at all:
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): FATAL EXCEPTION: main
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.Bitmap.nativeCreate(Native Method)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.Bitmap.createBitmap(Bitmap.java:468)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.Bitmap.createBitmap(Bitmap.java:435)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:488)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:462)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:346)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:372)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at com.androidnetwork.animlivewp.AnimatedLiveWallpaper$CubeEngine.<init>(AnimatedLiveWallpaper.java:55)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at com.androidnetwork.animlivewp.AnimatedLiveWallpaper.onCreateEngine(AnimatedLiveWallpaper.java:32)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.service.wallpaper.WallpaperService$IWallpaperEngineWrapper.executeMessage(WallpaperService.java:814)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:61)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.os.Handler.dispatchMessage(Handler.java:99)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.os.Looper.loop(Looper.java:123)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at android.app.ActivityThread.main(ActivityThread.java:4627)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at java.lang.reflect.Method.invokeNative(Native Method)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at java.lang.reflect.Method.invoke(Method.java:521)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
08-22 19:45:05.508: ERROR/AndroidRuntime(12277): at dalvik.system.NativeStart.main(Native Method)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
每次绘制画布时,仅创建一个位图并重新加载每个 png。例如,创建一个简单的例程,将每个图像重新加载到相同的位图分配中。我还建议您将 png 文件转换为 jpg 文件,因为 png 是无损格式。使用 jpg,您可以稍微压缩每一帧。
或者我想如果你想链接到 bootanimation
然后在你的 DrawAnim 代码中你可以使用这个
Only create one bitmap and reload each png each time you draw your canvas. For example create a simple routine that will reload each image into the same bitmap allocation. I would also suggest that you convert the png files into jpg files, as png are a lossless format. With jpg you can compress each frame slightly.
Or I guess you could use this if you want to link to the bootanimation
Then in your DrawAnim code just
android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340)
意味着,Android 会生成一个新的位图,因为您没有正确使用drawable-*dpi 文件夹...这可能会使您所需的值增加一倍内存使用情况android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340)
means, that Android generates a new Bitmap because you didn't use the drawable-*dpi folders correctly... that can double your needed memory usage您无法将这么多位图加载到内存中。
您可以将有限数量的位图加载到内存中,并在需要显示其他图片时使用。
您可以使用位图的 recycle() 方法释放一些内存,并创建新的位图。
如果等待GC进行垃圾回收,内存已经不够了。
关键是不要加载太多位图,并且当未在视图中显示时回收()。
You can't load so many bitmap into memory.
You can load limit number bitmap into memory, and when It is need to show other picture.
You can free some memory with bitmap's recycle() method, and create new bitmap.
If you wait the GC to garbage collection, the memory is already not enough.
The key point is don't load too many bitmap and recycle() when not show in view.
回到我上一家公司,我在移动平台上工作,我们遇到了一些相当严重的性能问题。我们进行了大量的调查,但我们通常发现一切都运行缓慢,并且很难确定原因。最终我们向芯片组供应商寻求帮助,了解为什么运行缓慢。一段时间后,他们给出了答案:“你执行了太多的代码。”
这是有一定道理的。
类似的答案可能适合这里:您使用了太多内存。
您只需要减少正在使用的位图的数量或大小。
Back at my last company, where I was working on a mobile platform, we were having some fairly serious performance issues. We did a fair amount of investigation, but we generally seeing that just everything was running slow and were having trouble determining why. Eventually we went to the chipset vendor for help on why things were running slow. After some time, they came back with the answer: "you are executing too much code."
There is a certain truth to that.
And a similar answer is probably appropriate here: you are using too much memory.
You just need to reduce the number or size of bitmaps you are using.