У меня огромная проблема с памятью в моем приложении. Я использую google map 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
s, это значит, что я не воссоздаю их, а повторно их использую. я могу ясно видеть, что каждый объект Bitmap
берется из кеша, а не из другого места. Однако, если Bitmap
еще не находится в кеше (загрузка в первый раз), я загружаю его из внутреннего хранилища моего приложения, но это происходит только тогда, когда Bitmap
является лазеризованным в первый раз. Я сохраняю экземпляр LruCache
в сохраненном экземпляре Fragment
и передаю его в свой пользовательский объект DefaultClusterRenderer<MyObject>
каждый раз, когда Activity
воссоздается, а карта должна быть перерисована.
это расширение 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);
память увеличивается с каждым поворотом экрана, чем больше я масштабирую карту (чем больше маркеров отображается), тем больше объема памяти добавляется в мою кучу приложения, когда я поворачиваю экран до тех пор, пока приложение не сработает.
иногда ошибка не такая, как показано выше, но показывает, что OOM произошла в этой строке в расширении DefaultClusterRenderer<MyItam>
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap));
.
Если я отключу значок пользовательского маркера (удалите весь связанный код Bitmap
), проблема с памятью исчезнет. пожалуйста, помогите мне найти причину появления этого OOM.