i have a huge memory problem in my app. i am using google map api v2 with ClusterManager
and custom markers. i supply an image via call to markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap));
for each marker based on its category. the problem is: after several screen rotations my app crashes because of OOM error:
我的应用程序存在很大的内存问题。我正在使用带有ClusterManager和自定义标记的谷歌map api v2。我通过调用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)
i have a LruCache
object with my Bitmap
s, it means that i do not recreate them, but reuse them. i can clearly see that every Bitmap
object is taken from the cache, not from elsewhere. However, if the Bitmap
is not in the cache yet (first time loading) i load it from my app's internal storage, but it only happnes when the Bitmap
is laoded the first time. i keep the instance of the LruCache
in a retained Fragment
instance and pass it to my custom DefaultClusterRenderer
object everytime the Activity
is recreated and map needs to be redrawn.
我有一个带有位图的LruCache对象,这意味着我不重新创建它们,而是重用它们。我可以清楚地看到,每个位图对象都是从缓存中获取的,而不是从其他地方获取的。但是,如果位图还没有在缓存中(第一次加载),我将从应用程序的内部存储中加载它,但只有在第一次加载位图时才会发生这种情况。我将LruCache实例保存在一个保留的片段实例中,并在每次重新创建活动并重新绘制映射时将其传递给我的自定义DefaultClusterRenderer
this is my DefaultClusterRenderer
extension:
这是我的DefaultClusterRenderer
public class DotRenderer extends DefaultClusterRenderer {
private final String internalStorageDir;
private final LruCache lruCache;
public DotRenderer(Context context, GoogleMap googleMap, ClusterManager clusterManager,
LruCache 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 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);
}
}
this is the code inside my Activity
where i start loading the map (this code is executed everytime screen orientation changes):
这是我在活动中开始加载地图的代码(每次屏幕方向改变时都执行此代码):
ClusterManager clusterManager = new ClusterManager<>(this, googleMap);
clusterManager.setOnClusterItemInfoWindowClickListener(
new ClusterManager.OnClusterItemInfoWindowClickListener() {
@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);
the memory keeps increasing with every screen rotation, the more i zoom in the map (the more markers are shown) the more amount of memory is added to my app's heap when i rotate the screen until application crashes.
内存随着屏幕的每一次旋转而不断增加,我在地图上缩放的次数越多(显示的标记越多),当我旋转屏幕直到应用程序崩溃时,添加到应用程序堆中的内存就越多。
sometimes the error is not like above, but shows that OOM happened at this line in my DefaultClusterRenderer
extension markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap));
.
有时错误与上面不同,但显示OOM发生在我的DefaultClusterRenderer
if i disable custom marker icon (remove all the Bitmap
related code) the memory problem vanishes. please help me to find what causes this OOM to appear.
如果我禁用自定义标记图标(删除所有位图相关代码),内存问题就会消失。请帮助我找出是什么原因导致这个OOM的出现。
1
I ran into this problem trying to run an app on demo mode for a few hours at a time. No matter what I tried, after 30 minutes, I would see this crash without a readable stack report.
我遇到了这个问题,试图在演示模式下运行一个应用程序,每次运行几个小时。不管我怎么尝试,30分钟后,我将看到这个崩溃,而没有一个可读的堆栈报告。
I tried System gc(), detaching the fragment, singleton activities, updating google play services to the latest, clearing references to overlays, attaching map lifecycle to activity and what not. After many failed attempts and a lot of frustration I finally found something that worked. It's not a fix for the map bug, but it kept my app from crashing:
我尝试了System gc(),分离片段、单例活动、更新谷歌播放服务到最新版本、清除对覆盖的引用、将映射生命周期附加到活动等等。经过多次失败的尝试和许多挫折,我终于找到了一些有用的东西。它不是地图bug的修复程序,但它阻止了我的应用程序崩溃:
0
Alright... I think this should work...
好吧……我认为这应该行得通……
Add googleMap.clear()
whenever you want to reset the map.
在需要重置映射时添加googleMap.clear()。
Hope this will help you.
希望这能对你有所帮助。