Google Maps Android API v2 Утечка памяти SupportMapFragment

Используя 2 простых действия. Первое действие, которое содержит только кнопку для запуска второго действия, содержащего карту:

Основная деятельность:

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

public void goToMap(View view){ //This is just the onClick method for the button
    Intent intent=new Intent( this, BigMapTest.class);
    startActivity(intent);
}

Активность карты:

public class BigMapTest extends FragmentActivity {
SupportMapFragment mapFragment;
GoogleMap map;

@Override
protected void onCreate(Bundle arg0) {
    // TODO Auto-generated method stub
    super.onCreate(arg0);

    setContentView(R.layout.travel_diary_big_map);

    mapFragment=(SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.big_map);
    map=mapFragment.getMap();

}

XML-макет для действия карты:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<fragment
        android:id="@+id/big_map"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        class="com.google.android.gms.maps.SupportMapFragment"  
        />

Now when I run this code, pressing the button to move to the Activity with the map, and pressing back to get to the first activity...then repeating the process, I can see the heap increasing in size each time, until it reaches it's limits and then it starts clamping. If you mess around a bit more with map(i.e. zooming) I can get an OOM Exception at this point.

01-25 16:10:13.931: D/dalvikvm(21578): GC_FOR_ALLOC освобождено 1898 КБ, 7% свободно 45859 КБ/49187 КБ, приостановлено на 204 мс
01-25 16:10:14.671: I/dalvikvm-heap(21578) : Ограничить целевую кучу сборщика мусора с 52,724 МБ до 48,000 МБ
01-25 16:10:14.671: D/dalvikvm(21578): GC_CONCURRENT освободил 2534 КБ, 6 % свободно 46554 КБ/49187 КБ, пауза 3 мс+14 мс
01–25 16:10:15.372: I/dalvikvm-heap(21578): ограничение целевой кучи GC с 52,979 МБ до 48 000 МБ
01–25 16:10:15.382: D/dalvikvm(21578): GC_CONCURRENT освобожден 2273K, 5 % свободно 46815K/49187K, пауза 3 мс+15 мс
01–25 16:10:15.622: I/dalvikvm-heap(21578): ограничение целевой кучи GC с 52,604 МБ до 48,000 МБ
01 -25 16:10:15.622: D/dalvikvm(21578): GC_FOR_ALLOC освободил 657K, 6% освободил 46431K/49187K, приостановил 202ms
01-25 16:10:16.203: I/dalvikvm-heap(21578): Ограничить целевой объем кучи GC с 52,959 МБ до 48 000 МБ
01–25 16:10:16.203: D/dalvikvm(21578): GC_FOR_ALLOC освободил 1 469 КБ, 5 % освободил 46 796 К/49 187 К, приостановил 217 мс
01–25 16:10:16.203: I/dalvikvm-heap(21578): Принудительный сбор n из SoftReferences для распределения 278744 байта
01-25 16:10:16.423: I/dalvikvm-heap(21578): ограничение целевой кучи GC с 52,952 МБ до 48,000 МБ
01-25 16:10 :16.423: D/dalvikvm(21578): GC_BEFORE_OOM освобождено 9 КБ, 5 % свободно 46786 КБ/49187 КБ, пауза 219 мс
01–25 16:10:16.423: E/dalvikvm-heap(21578): Недостаточно памяти на Выделение 278744 байт.

Любые предложения/помощь будут оценены.


person Nims    schedule 25.01.2013    source источник
comment
Используйте MAT, чтобы определить источник вашей утечки.   -  person CommonsWare    schedule 25.01.2013
comment
Используя MAT, а затем ища объект с наибольшим сохраненным размером (который продолжает расти с каждым циклом), затем используя путь к корням GC, исключая слабые ссылки, я достиг следующего: class maps.by.a @ 0x414f7fa8 System Class. - Но я не знаю, что делать дальше.   -  person Nims    schedule 25.01.2013
comment
Кажется, что-то накапливается в Hashmap: класс maps.by.a, загруженный dalvik.system.PathClassLoader @ 0x413de740, занимает 12 923 112 (57,78%) байт. Память накапливается в одном экземпляре java.util.HashMap$HashMapEntry[], загружаемом ‹загрузчиком системных классов›. Ключевые слова java.util.HashMap$HashMapEntry[] maps.by.a dalvik.system.PathClassLoader @ 0x413de740   -  person Nims    schedule 25.01.2013
comment
Ну, это звучит не многообещающе. Есть ли шанс, что я смогу уговорить вас загрузить куда-нибудь весь тестовый проект, чтобы я мог на него взглянуть?   -  person CommonsWare    schedule 25.01.2013
comment
Конечно. Хотя я очень новичок в этом... Где было бы уместно загрузить его тоже? (Хотя проект очень маленький, как описано выше)   -  person Nims    schedule 25.01.2013
comment
Репозиторий GitHub, ZIP-файл в общедоступном DropBox, ZIP-файл на Amazon S3, ...   -  person CommonsWare    schedule 25.01.2013
comment
dl.dropbox.com/u/83195107/MapMemoryLeakTest.zip   -  person Nims    schedule 25.01.2013
comment
Хорошо, на выходных посмотрю и отпишусь о результатах.   -  person CommonsWare    schedule 25.01.2013
comment
Google признал эту проблему и заявил, что она будет исправлена ​​в следующем выпуске Maps Android API. code.google.com/p/gmaps-api- вопросы/вопросы/подробности?id=4766   -  person Nims    schedule 01.02.2013
comment
@Nims Вопрос - когда следующий релиз?   -  person theblitz    schedule 06.02.2013


Ответы (4)


Насколько я могу судить по базовому анализу MAT, то, что вы видите, — это кэш, поддерживаемый Maps V2 загруженных картографических данных. Кэш кажется больше, если вы много панорамируете и масштабируете. Кэш уменьшается, если вы покинете карту и вернетесь к новой карте позже. Я не мог получить N кешей из N раз, запуская активность карты вашего примера приложения, а размер кеша уменьшался и уменьшался в зависимости от того, что делал пользователь.

Увы, этот кеш нельзя настроить, насколько я знаю, с точки зрения того, насколько он велик, когда он очищается, перетекает ли он на диск и т. д.

Таким образом, по умолчанию все, что вы можете сделать, это оставить здоровый кусок пространства кучи для Maps V2, чтобы поиграть с ним, и предпринять шаги, чтобы оставаться в пределах этого меньшего подмножества кучи.

Если вы хотите поэкспериментировать, вы можете попробовать вызвать clear() на GoogleMap или onLowMemory() на SupportMapFragment, чтобы узнать, поможет ли это уменьшить размер кэша.

person CommonsWare    schedule 26.01.2013
comment
Хотя отчасти наблюдаемое поведение может быть связано с приливами и отливами кеша, фрагмент карты определенно дает утечку контекста, в котором он размещен. Это можно легко проверить, несколько раз повернув действие с картой, а затем посмотрев, сколько копий действия все еще находится в куче. Даже демонстрационное приложение карт делает это, как описано в ошибке. отчет. - person blahdiblah; 26.02.2013

Разработчики Android 17:10 – Страница Google+

Мы выпускаем сервисы Google Play версии 3.0, в которой представлены улучшения входа в Google+ и Google Maps Android API.

По состоянию на 26 февраля 2013 г. эта проблема также описана в страница gmaps-api-issues теперь исправлена ​​текущим Обновление API Google.

person lambda    schedule 26.02.2013

У меня точно такая же проблема. Объем памяти увеличивается каждый раз, когда запускается действие, в котором размещается карта V2. И он не освобождается, даже когда действие заканчивается.

Таким образом, обходным путем является повторное использование этой активности. Сделайте активность singleTask в манифесте и вместо finish() используйте moveTaskToBack(true);

person Chonnarong Hanyawongse    schedule 30.01.2013
comment
Это хорошее решение, если у вас есть только 1 карта или используется очень мало карт, но я не понимаю, как это будет работать, если у вас есть карты в нескольких действиях. - person Nims; 01.02.2013

Используйте это в макете:

<LinearLayout
        android:id="@+id/map_container2"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_weight="35.22"
        android:orientation="horizontal" >

        <fragment
            android:id="@+id/map1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            class="com.google.android.gms.maps.SupportMapFragment"
            map:cameraTargetLat="40.72"
            map:cameraTargetLng="-74.00"
            map:cameraZoom="8" />
    </LinearLayout>

И этот код:

onCreate{
   setUpMapIfNeeded();
}

private void setUpMapIfNeeded() {
        // TODO Auto-generated method stub
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) {
            // Try to obtain the map from the SupportMapFragment.
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map1))
                    .getMap();
            // Check if we were successful in obtaining the map.
            if (mMap != null) {
                setUpMap();
            }
        }
    }

private void setUpMap() {
        // TODO Auto-generated method stub
         // Hide the zoom controls as the button panel will cover it.
        mUiSettings = mMap.getUiSettings();
 // Enables/disables zoom gestures (i.e., double tap, pinch & stretch).
        mMap.getUiSettings().setZoomGesturesEnabled(false);
// Enables/disables scroll gestures (i.e. panning the map).
        mMap.getUiSettings().setScrollGesturesEnabled(false);
 // Enables/disables the compass (icon in the top left that indicates the orientation of the
        // map).
        mMap.getUiSettings().setCompassEnabled(false);
        // Add lots of markers to the map.
        addMarkersToMap();

        // Pan to see all markers in view.
        // Cannot zoom to bounds until the map has a size.
        final View mapView = getSupportFragmentManager().findFragmentById(R.id.map1).getView();
        if (mapView.getViewTreeObserver().isAlive()) {
            mapView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                @SuppressLint("NewApi") // We check which build version we are using.
                @Override
                public void onGlobalLayout() {
                    LatLngBounds bounds = new LatLngBounds.Builder()
                            .include(WOODS)
                            .build();
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                      mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    } else {
                      mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    }
                    mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
                }
            });
        }
    }

private void addMarkersToMap() {
        // TODO Auto-generated method stub
         // Uses a colored icon.
        mWoods = mMap.addMarker(new MarkerOptions()
                .position(WOODS)
                .title("Woods")
                .snippet("R. Quatá, 1016, Vila Olimpia - (11) 3849-6868")
                .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE)));
    }
person Marckaraujo    schedule 25.01.2013
comment
Что такое WOODS здесь LatLngBounds bounds = new LatLngBounds.Builder() .include(WOODS) и имя переменной для mUiSettings - person Singh Arjun; 01.06.2013
comment
WOODS является частным static final LatLng WOODS = new LatLng(-23.596089,-46.682393); переменная для значения Lat и Long, private UiSettings mUiSettings; - person Marckaraujo; 01.06.2013
comment
Можете ли вы объяснить, почему это устраняет утечки? - person Daniel Gomez Rico; 07.02.2020