我在我的应用程序中遇到了一个内存问题。我正在使用Google地图API V2和ClusterManager
以及自定义标记。我为每个标记提供一个基于其类别的图像,通过调用markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap));
来实现。问题是:在多次屏幕旋转后,我的应用程序由于OOM错误而崩溃:
05-14 11:04:12.692 14020-30201/rokask.rideabike E/art﹕ Throwing OutOfMemoryError "Failed to allocate a 4194316 byte allocation with 1627608 free bytes and 1589KB until OOM"
05-14 11:04:12.722 14020-30201/rokask.rideabike E/AndroidRuntime﹕ FATAL EXCEPTION: GLThread 19179
Process: rokask.rideabike, PID: 14020
java.lang.OutOfMemoryError: Failed to allocate a 4194316 byte allocation with 1627608 free bytes and 1589KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:939)
at android.graphics.Bitmap.createBitmap(Bitmap.java:912)
at android.graphics.Bitmap.createBitmap(Bitmap.java:879)
at com.google.maps.api.android.lib6.gmm6.n.c.i.a(Unknown Source)
at com.google.maps.api.android.lib6.gmm6.n.c.l.a(Unknown Source)
at com.google.maps.api.android.lib6.gmm6.n.c.l.a(Unknown Source)
at com.google.maps.api.android.lib6.gmm6.n.c.l.b(Unknown Source)
at com.google.maps.api.android.lib6.gmm6.n.c.b.ak.a(Unknown Source)
at com.google.maps.api.android.lib6.gmm6.n.c.b.as.a(Unknown Source)
at com.google.maps.api.android.lib6.gmm6.n.x.a(Unknown Source)
at com.google.maps.api.android.lib6.gmm6.n.l.a(Unknown Source)
at com.google.maps.api.android.lib6.gmm6.n.l.b(Unknown Source)
at com.google.maps.api.android.lib6.gmm6.n.cv.f(Unknown Source)
at com.google.maps.api.android.lib6.gmm6.n.cv.run(Unknown Source)
我有一个带有LruCache
对象的Bitmap
,这意味着我不会重新创建它们,而是重复使用它们。我可以清晰地看到每个Bitmap
对象都是从缓存中获取的,而不是其他地方获取的。但是,如果Bitmap
尚未在缓存中(第一次加载),我会从我的应用程序内部存储加载它,但仅在第一次加载Bitmap
时才发生这种情况。我将LruCache
实例保留在一个保留的Fragment
实例中,并在每次重新创建活动并需要重新绘制地图时将其传递给我的自定义DefaultClusterRenderer<MyObject>
对象。这是我的
DefaultClusterRenderer<MyItem>
扩展:public class DotRenderer extends DefaultClusterRenderer<Dot> {
private final String internalStorageDir;
private final LruCache<String, Bitmap> lruCache;
public DotRenderer(Context context, GoogleMap googleMap, ClusterManager<Dot> clusterManager,
LruCache<String, Bitmap> lruCache, String internalStorageDir)
{
super(context, googleMap, clusterManager);
//this.bitmaps = bitmaps;
this.internalStorageDir = internalStorageDir;
this.lruCache = lruCache;
}
@Override
protected void onBeforeClusterItemRendered(Dot mapObject, MarkerOptions markerOptions) {
markerOptions.title(mapObject.getTitle());
String id = Integer.toString(mapObject.getTypeId());
//
Bitmap bitmap = getBitmapFromMemCache(id);
if (bitmap == null) {
Log.d(MainActivity.LOG_TAG, "reading bitmap from storage.");
Map.Entry<String, Bitmap> bitmapEntry
= BitmapManager.getBitmapFromStorage(internalStorageDir, id);
if (bitmapEntry != null) {
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmapEntry.getValue()));
addBitmapToMemCache(id, bitmapEntry.getValue());
}
} else {
Log.d(MainActivity.LOG_TAG, "reading bitmap from cache.");
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap));
}
}
private void addBitmapToMemCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
lruCache.put(key, bitmap);
}
}
private Bitmap getBitmapFromMemCache(String key) {
return lruCache.get(key);
}
}
这是我Activity
中启动加载地图的代码(每次屏幕方向更改时都会执行此代码):
ClusterManager<Dot> clusterManager = new ClusterManager<>(this, googleMap);
clusterManager.setOnClusterItemInfoWindowClickListener(
new ClusterManager.OnClusterItemInfoWindowClickListener<Dot>() {
@Override
public void onClusterItemInfoWindowClick(Dot dot) {
int id = dot.getId();
String title = dot.getTitle();
Log.d(LOG_TAG, "clicked marker with id " + id
+ " and title " + title + ".");
Intent infoWindowActivityIntent =
new Intent(MainActivity.this, InfoWindowActivity.class);
infoWindowActivityIntent.putExtra("dotId", id);
infoWindowActivityIntent.putExtra("dotTitle", title);
startActivity(infoWindowActivityIntent);
}
});
googleMap.setOnCameraChangeListener(clusterManager);
googleMap.setOnInfoWindowClickListener(clusterManager);
DotRenderer dotRenderer =
new DotRenderer(getApplicationContext(), googleMap, clusterManager,
lruCache, this.getFilesDir().toString());
clusterManager.setRenderer(dotRenderer);
每次旋转屏幕,地图上显示的标记越多(缩放程度越高),应用程序的堆中就会添加更多的内存,直到应用程序崩溃。
有时错误信息不像上面那样,而是在我的 DefaultClusterRenderer<MyItam>
扩展中显示 OOM 发生在这行:markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap));
。
如果禁用自定义标记图标(删除所有与 Bitmap
相关的代码),内存问题就会消失。请帮我找出导致 OOM 出现的原因。
System.gc()
,它没什么帮助... 我猜它们差不多。 - SalivanMapView
,我使用GoogleMap
。 - Salivan