web-dev-qa-db-ja.com

グーグルマップapiv2メモリ不足エラー

アプリに大きなメモリの問題があります。 ClusterManagerとカスタムマーカーを使用してgooglemap apiv2を使用しています。カテゴリに基づいて、各マーカーの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)
_

LruCachesを持つBitmapオブジェクトがあります。つまり、オブジェクトを再作成せずに再利用します。すべてのBitmapオブジェクトが他の場所からではなく、キャッシュから取得されていることがはっきりとわかります。ただし、Bitmapがまだキャッシュにない場合(初回ロード)、アプリの内部ストレージからロードしますが、Bitmapが最初にロードされたときにのみ発生します。 LruCacheのインスタンスを保持されたFragmentインスタンスに保持し、Activityが再作成され、マップを再描画する必要があるたびに、カスタム_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>_拡張子markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap));のこの行でOOMが発生したことを示しています。

カスタムマーカーアイコンを無効にすると(すべてのBitmap関連コードを削除)、メモリの問題はなくなります。このOOMが表示される原因を見つけるのを手伝ってください。

17
Salivan

デモモードでアプリを一度に数時間実行しようとすると、この問題が発生しました。何を試しても、30分後、読み取り可能なスタックレポートなしでこのクラッシュが発生しました。

System gc()を試し、フラグメントのデタッチ、シングルトンアクティビティ、Google Play開発者サービスの最新版への更新、オーバーレイへの参照のクリア、アクティビティへのマップライフサイクルのアタッチなどを行いました。多くの失敗した試みと多くの欲求不満の後、私はついにうまくいく何かを見つけました。これはマップのバグの修正ではありませんが、アプリがクラッシュするのを防ぎました。

    <application
    ...
    Android:largeHeap="true">
2
TacoEater

了解しました...これでうまくいくと思います...

マップをリセットするときはいつでもgoogleMap.clear()を追加してください。

これがお役に立てば幸いです。

1
Rishad Appat