当我将Android应用最小化约4或5次时,我总是会收到以下错误:
02-01 19:24:11.980: E/dalvikvm-heap(22362): Out of memory on a 3686416-byte allocation.
02-01 19:24:12.000: E/dalvikvm(22362): Out of memory: Heap Size=62755KB, Allocated=55237KB, Limit=65536KB
02-01 19:24:12.000: E/dalvikvm(22362): Extra info: Footprint=62435KB, Allowed Footprint=62755KB, Trimmed=2144KB
02-01 19:24:12.000: E/Bitmap_JNI(22362): Create Bitmap Failed.
02-01 19:24:12.000: E/Bitmap_JNI(22362): Failed to create SkBitmap!
02-01 19:24:12.000: E/AndroidRuntime(22362): FATAL EXCEPTION: main
02-01 19:24:12.000: E/AndroidRuntime(22362): java.lang.OutOfMemoryError: (Heap Size=62755KB, Allocated=55237KB)
02-01 19:24:12.000: E/AndroidRuntime(22362): at android.graphics.Bitmap.nativeCreateScaledBitmap(Native Method)
02-01 19:24:12.000: E/AndroidRuntime(22362): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:744)
02-01 19:24:12.000: E/AndroidRuntime(22362): at de.vauge.mb.Utils.getResizedBitmap(Utils.java:56)
02-01 19:24:12.000: E/AndroidRuntime(22362): at de.vauge.mb.MenuView.initialize(MenuView.java:74)
02-01 19:24:12.000: E/AndroidRuntime(22362): at de.vauge.mb.MenuView$1.handleMessage(MenuView.java:137)
02-01 19:24:12.000: E/AndroidRuntime(22362): at android.os.Handler.dispatchMessage(Handler.java:99)
02-01 19:24:12.000: E/AndroidRuntime(22362): at android.os.Looper.loop(Looper.java:156)
02-01 19:24:12.000: E/AndroidRuntime(22362): at android.app.ActivityThread.main(ActivityThread.java:5045)
02-01 19:24:12.000: E/AndroidRuntime(22362): at java.lang.reflect.Method.invokeNative(Native Method)
02-01 19:24:12.000: E/AndroidRuntime(22362): at java.lang.reflect.Method.invoke(Method.java:511)
02-01 19:24:12.000: E/AndroidRuntime(22362): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
02-01 19:24:12.000: E/AndroidRuntime(22362): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
02-01 19:24:12.000: E/AndroidRuntime(22362): at dalvik.system.NativeStart.main(Native Method)
我的应用程序只包含一个Activity,它有7个不同的,自编的视图(所有这些视图都包含一些位图),并且当它们不需要时它们被切换为不可见(可能不是很好的样式,但它适用于我到现在为止......)这些视图中的每一个都有一个destroy() - 函数,它循环使用它中的所有Bitmaps,MainActivity的onDestroy()调用所有那些destroy() - 函数。此外,我没有使用任何静态位图。
那么,除了回收所有位图而不使用静态位图之外,还有什么我可以尝试的吗?
答案 0 :(得分:9)
好。 Android上的位图可能有点棘手。您能否提供有关位图来源及其大小的更好信息?
否则,我建议你研究这些事情:
如果您要加载远程图像,请查看fresco。您还可以查看Picasso。我个人过去喜欢ImageLoader,但它不再被维护了。
如果您正在使用曾经是推荐选项的inPurgable标志,请尝试找到一种解决方法,因为它实际上会为每个图像分配更多内存。
如果您经常解码小型本地资产,请考虑将您的drawable保存在hashmap中,并在需要时重复使用它们。减去GC。
如果您关心应用程序的子类,您可以使用OnLowMemory调用来了解您可能真正需要清理的时间(主要用于调试,而不是现实生活)情况)......如果那还不晚......:)
看看Chris Banes的博客。 This是一个非常有趣的内存缓存解决方案
实现您需要和可能时调用的内存修整器。
另一个不足为奇的优化是在您可以使用较小的对象时...考虑一下您的最小数据模型和图像大小,并尝试为这些对象提供一致的API。
答案 1 :(得分:1)
对于Ben Max评论的#3,我提出了两个有用的课程:
public abstract class SoftReferenceStorage<K, V>{
private static HashMap<Object, SoftReference<Object>> objectsHash = new HashMap<Object, SoftReference<Object>>();
@SuppressWarnings("unchecked")
public V get(K key) {
if (objectsHash.containsKey(key)) {
SoftReference<Object> ref = objectsHash.get(key);
if (ref.get() == null) {
objectsHash.put(key, new SoftReference<Object>(createValueForKey(key)));
return (V)objectsHash.get(key).get();
} else {
return (V)ref.get();
}
} else {
objectsHash.put(key, new SoftReference<Object>(createValueForKey(key)));
return (V)objectsHash.get(key).get();
}
}
protected abstract V createValueForKey(K key);
}
和
public class FrequentlyUsedBitmapResources extends SoftReferenceStorage<Integer, Bitmap>{
private static FrequentlyUsedBitmapResources instance = null;
private Resources resources;
public FrequentlyUsedBitmapResources(Resources resources) {
super();
this.resources = resources;
}
public static FrequentlyUsedBitmapResources getInstance() {
if (instance == null) {
instance = new FrequentlyUsedBitmapResources(HiDriveApp.getContext().getResources());
}
return instance;
}
@Override
protected Bitmap createValueForKey(Integer resId) {
return BitmapFactory.decodeResource(resources, resId);
}
}
可以像:
一样使用Bitmap b = FrequentlyUsedBitmapResources.getInstance().get(R.drawable.overview_photo_placeholder);
答案 2 :(得分:0)
确保您在onCreate()
而不是onStart()
或onResume()
中加载它们。这听起来好像每次你恢复时都会重新加载,但它们并没有被销毁,因为当你最小化应用程序时没有调用onDestroy()
。
答案 3 :(得分:0)
如果图像在本地设备上(即与您的代码合并或来自用户图像库),那么我可以选择不让它们变得不可见或不是,而是只是将它们带入飞行中从磁盘。事实证明,所有这些设备基本上都是基于闪存的,与主轴磁盘相比非常快。最有可能的是,用户无法感受到图像的磁盘IO性能损失。
这样,您也可以在任何时候限制内存中的AMOUNT个图像。
我同意你也应该考虑蒂姆的评估。