Android - множество исключений OutOfMemoryError только для одной операции с MapView - PullRequest
0 голосов
/ 21 августа 2011

Я получаю довольно много отчетов OutOfMemoryError от моих пользователей, и каждый отдельный отчет относится к одной и той же активности, которая содержит MapView.Я думаю, что это единичное исключение только с одним местом в моем приложении, и я не могу понять, в чем проблема.Кто-нибудь может дать мне несколько советов относительно того, почему это происходит?

Я удалил некоторый ненужный код для этого вопроса, поэтому, если кто-то думает, что проблема может быть в этом, я опубликую его.

Следы стека

Stack Trace #1

java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:677)
at com.google.android.maps.ZoomHelper.createSnapshot(ZoomHelper.java:444)
at com.google.android.maps.ZoomHelper.beginZoom(ZoomHelper.java:194)
at com.google.android.maps.MapView$2.onScaleBegin(MapView.java:371)
at android.view.ScaleGestureDetector.onTouchEvent(ScaleGestureDetector.java:216)
at com.google.android.maps.MapView.onTouchEvent(MapView.java:646)
at android.view.View.dispatchTouchEvent(View.java:3778)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:920)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:959)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:959)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:959)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1716)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1124)
at android.app.Activity.dispatchTouchEvent(Activity.java:2125)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1700)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1822)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:143)
at android.app.ActivityThread.main(ActivityThread.java:5068)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at dalvik.system.NativeStart.main(Native Method)


Stack Trace #2

java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:468)
at com.google.android.maps.ZoomHelper.createSnapshot(ZoomHelper.java:444)
at com.google.android.maps.ZoomHelper.doZoom(ZoomHelper.java:151)
at com.google.android.maps.ZoomHelper.doZoom(ZoomHelper.java:140)
at com.google.android.maps.MapView.doZoom(MapView.java:1478)
at com.google.android.maps.MapView.doZoom(MapView.java:1487)
at com.google.android.maps.MapController.zoomOut(MapController.java:439)
at com.hookedroid.fishingcompanion.GoogleMaps$3.onClick(GoogleMaps.java:133)
at android.view.View.performClick(View.java:2405)
at android.view.View$PerformClick.run(View.java:8813)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4627)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)


Stack Trace #3

java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:468)
at android.graphics.Bitmap.createBitmap(Bitmap.java:435)
at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340)
at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:488)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:462)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:346)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:372)
at com.hookedroid.fishingcompanion.maps.CrosshairOverlay.draw(CrosshairOverlay.java:32)
at com.google.android.maps.OverlayBundle.draw(OverlayBundle.java:45)
at com.google.android.maps.MapView.onDraw(MapView.java:494)
at android.view.View.draw(View.java:6742)
at android.view.ViewGroup.drawChild(ViewGroup.java:1640)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
at android.view.View.draw(View.java:6745)
at android.widget.FrameLayout.draw(FrameLayout.java:352)
at android.view.ViewGroup.drawChild(ViewGroup.java:1640)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
at android.view.View.draw(View.java:6745)
at android.widget.FrameLayout.draw(FrameLayout.java:352)
at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1913)
at android.view.ViewRoot.draw(ViewRoot.java:1407)
at android.view.ViewRoot.performTraversals(ViewRoot.java:1163)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1727)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4646)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at dalvik.system.NativeStart.main(Native Method)

Активность

public class GoogleMaps extends MapActivity {

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.googlemaps_layout);

    intent = getIntent();
    currentMode = intent.getIntExtra("MAP_MODE", 0);

    initControls();
    initMembers();

    currentOverlayMode = prefs.getInt("map_viewmode", 0);

    populateMap();
}

private void initMembers() {
    mDbHelper = new FishingCompanionDB(this);
    mDbHelper.open();
    catchList = new ArrayList<FishEntry>();

    prefs = PreferenceManager.getDefaultSharedPreferences(this);
    prefsEditor = prefs.edit();
}

private void initControls() {
    mMaps = (MapView)findViewById(R.id.google_maps);
    mMaps.setClickable(true);
    mMaps.setLongClickable(true);
    mapController = mMaps.getController();

    mOverlayModeBtn = (Button)findViewById(R.id.googlemaps_overlay_btn);
    mOverlayModeBtn.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            if (currentOverlayMode < 1)
                currentOverlayMode++;
            else
                currentOverlayMode = 0;
            switch (currentOverlayMode) {
                case OVERLAY_STREET:
                    mMaps.setSatellite(false);
                    mMaps.setStreetView(true);
                    prefsEditor.putInt("map_viewmode", OVERLAY_STREET);
                    break;
                case OVERLAY_SAT:
                    mMaps.setStreetView(false);
                    mMaps.setSatellite(true);
                    prefsEditor.putInt("map_viewmode", OVERLAY_SAT);
                    break;
            }
            prefsEditor.commit();
            mMaps.invalidate();
        }
    });
    mZoomInBtn = (Button)findViewById(R.id.googlemaps_btn_zoomin);
    mZoomInBtn.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            mapController.zoomIn();
        }
    });
    mZoomOutBtn = (Button)findViewById(R.id.googlemaps_btn_zoomout);
    mZoomOutBtn.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            mapController.zoomOut();
        }
    });
}

private void populateMap() {
    overlays = mMaps.getOverlays();
    overlays.clear();

    overlays.add(new CrosshairOverlay(this, R.drawable.mapcenter));
    mSelectPos = (Button)findViewById(R.id.googlemaps_select_location);
    mSelectPos.setVisibility(View.VISIBLE);
    mSelectPos.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            GeoPoint centerGp = mMaps.getMapCenter();
            double lat = centerGp.getLatitudeE6()/1E6;
            double lng = centerGp.getLongitudeE6()/1E6;

            Intent i;
            if (currentMode == SELECT_POS_WEATHER) {
                i = new Intent(GoogleMaps.this, WeatherLookup.class);
                i.putExtra("WEATHER_LAT", lat);
                i.putExtra("WEATHER_LNG", lng);
            }
            else {
                i = new Intent(GoogleMaps.this, AddLocation.class);
                i.putExtra("LOCATION_LAT", lat);
                i.putExtra("LOCATION_LNG", lng);
            }
            i.putExtra("MODE", 1);

            startActivity(i);
            finish();
        }
    });

    mMaps.invalidate();
}

@Override
protected boolean isRouteDisplayed() {
    return false;
}
}

Наложение перекрестия

public class CrosshairOverlay extends Overlay {

    private Context mContext;
    private int resourceId;

    public CrosshairOverlay(Context context, int resId) {
        this.mContext = context;
        this.resourceId = resId;
    }

    public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) {
         super.draw(canvas, mapView, shadow);
         GeoPoint centerGp = mapView.getMapCenter();
         Projection projection = mapView.getProjection();
         Point centerPoint = projection.toPixels(centerGp, null);
         Paint p = new Paint();
         Bitmap bmp = BitmapFactory.decodeResource(mContext.getResources(), resourceId);

         canvas.drawBitmap(bmp, (centerPoint.x - (bmp.getWidth()/2)), (centerPoint.y - (bmp.getHeight()/2)), p);

         return true;
    }
}

DUMPSYS MEMINFO

** MEMINFO in pid 25493 [com.hookedroid.fishingcompanion] **
native   dalvik    other    total
size:    10036     7495      N/A    17531
allocated:     9955     3965      N/A    13920
free:       80     3530      N/A     3610
(Pss):     3717     1480     6703    11900
(shared dirty):      668     1512     8056    10236
(priv dirty):     3696      804     5024     9524

Objects
Views:        0        ViewRoots:        0
AppContexts:        0       Activities:        0
Assets:        3    AssetManagers:        3
Local Binders:       19    Proxy Binders:       21
Death Recipients:        1
OpenSSL Sockets:        0

SQL
heap:      527         MEMORY_USED:      527
PAGECACHE_OVERFLOW:       62         MALLOC_SIZE:       50

DATABASES
pgsz     dbsz   Lookaside(b)  Dbname
1       16            260  FishingCompanion
1       18             63  google_analytics.db

Ответы [ 2 ]

7 голосов
/ 23 августа 2011

Я скачал приложение и поиграл с ним, наблюдая за памятью через dumpsys.

Все выглядит нормально, память восстанавливается каждый раз.Я не могу воспроизвести ситуацию, но я вижу один всплеск, который, вероятно, связан.Всякий раз, когда вы перемещаетесь по карте или изменяете масштаб изображения (в основном, обновляя плитки) на спутнике, в памяти будет небольшой всплеск.Если вы сделаете это достаточно быстро, у вас не будет шанса на то, чтобы его восстановили, и он будет расти.

Теперь, мой телефон - Android 3.3.4 и имеет довольно хорошую конфигурацию, так что, возможно, GC намного эффективнее.Интересно, хотя, если бы мои старые тестовые телефоны восстанавливали память медленнее и, таким образом, когда я добирался до карты (скажем, после добавления рыб), у меня все еще была бы память от предыдущего действия, которое не было восстановлено GC.Затем, что бы я ни делал, я бы пошел в свое место и проверил все, увеличив / уменьшив масштаб.Это в сочетании с предыдущей памятью из предыдущих действий может довести телефоны до предела.

Это всего лишь теория, я в пути и не имею доступа ко всем моим тестовым телефонам.Вы знаете, какие версии телефонов, которые вылетали?Я вернусь через 3-4 дня и смогу попробовать приложение на своих старых телефонах.

ОБНОВЛЕНО: Я провел больше экспериментов с этим приложением.Я почти уверен, что добавление рыб непрерывно добавит больше воспоминаний.Я продолжал добавлять и удалять рыб и проверял, что память продолжает расти с помощью dumpsys meminfo.Реальные пользователи Pro Edition или даже облегченные пользователи, которые продолжают добавлять и удалять рыб, могут в конечном итоге приблизиться к пределу, и переход к карте впоследствии вызовет ошибку отсутствия памяти, поскольку в карту будет добавлен скачок памяти.Вот снимок после того, как я несколько раз добавил и удалил рыб

** MEMINFO in pid 11572 [com.hookedroid.fishingcompanion.lite] **
                    native   dalvik    other    total    limit   bitmap nativeBmp
            size:    19728    18251      N/A    37979    32768      N/A      N/A
       allocated:    17174    14674      N/A    31848      N/A     3144        0
            free:      405     3577      N/A     3982      N/A      N/A      N/A
           (Pss):    12750     1771    25944    40465      N/A      N/A      N/A
  (shared dirty):      908     1544     5800     8252      N/A      N/A      N/A
    (priv dirty):    12732     1008    24208    37948      N/A      N/A      N/A

Ваша личная память увеличится до 37 948, что, я уверен, если я продолжу добавлять и удалять рыб, это в конечном итоге вызовет исключение OutOfMemoryException

БОЛЬШЕ ОБНОВЛЕНИЯ (несколько минут спустя): Мне удается вывести приложение из строя, используя приведенную выше теорию.Я должен был добавить и удалить рыб несколько раз, прежде чем это произойдет.До сбоя приложения может быть более 50 рыб.

Я предполагаю, что SQL почему-то не был очищен должным образом.Глядя на dumpsys после каждого набора добавления и удаления 10 рыб (что является пределом облегченной версии), я вижу, что

SQL
               heap:     6581         MEMORY_USED:     6581
 PAGECACHE_OVERFLOW:      173         MALLOC_SIZE:       50

 DATABASES
      pgsz     dbsz   Lookaside(b)  Dbname
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             33  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion
         1       16             62  FishingCompanion

Память SQL продолжает расти, даже если я уже удалил рыб.Если я продолжу делать это в течение некоторого времени, в конечном итоге он достигнет верхней границы телефона, и переход к карте (что вызывает скачок в памяти) вызовет исключение нехватки памяти, по-видимому, указывающее, что страница карты является причиной, тогда какЯ думаю, что страница добавления / удаления рыбы является частью реальной причины (я говорю «часть реальной причины», поскольку я не знаю, произойдет ли подобный эффект, скажем, если я добавлю новое местоположение).

Я правильно понял OutMemoryException, когда общий объем памяти составляет около 58 МБ (это, вероятно, отличается от телефона к телефону).Для справки вот похожее исключение OutOfMemoryException, которое я получил:

D/dalvikvm(11572): GC_FOR_MALLOC freed 125K, 11% free 25734K/28743K, external 4047K/4695K, paused 188ms
D/AndroidRuntime(11572): Shutting down VM
W/dalvikvm(11572): threadid=1: thread exiting with uncaught exception (group=0x4001d648)
E/AndroidRuntime(11572): FATAL EXCEPTION: main
E/AndroidRuntime(11572): java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=28743KB, Allocated=25734KB, Bitmap Size=4047KB)
E/AndroidRuntime(11572):    at android.graphics.Bitmap.nativeCreate(Native Method)
E/AndroidRuntime(11572):    at android.graphics.Bitmap.createBitmap(Bitmap.java:695)
E/AndroidRuntime(11572):    at com.google.android.maps.ZoomHelper.createSnapshot(ZoomHelper.java:444)
E/AndroidRuntime(11572):    at com.google.android.maps.ZoomHelper.beginZoom(ZoomHelper.java:194)
E/AndroidRuntime(11572):    at com.google.android.maps.MapView$2.onScaleBegin(MapView.java:380)
E/AndroidRuntime(11572):    at android.view.ScaleGestureDetector.onTouchEvent(ScaleGestureDetector.java:216)
E/AndroidRuntime(11572):    at com.google.android.maps.MapView.onTouchEvent(MapView.java:682)
E/AndroidRuntime(11572):    at android.view.View.dispatchTouchEvent(View.java:3932)
E/AndroidRuntime(11572):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:955)
E/AndroidRuntime(11572):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1015)
E/AndroidRuntime(11572):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1015)
E/AndroidRuntime(11572):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1015)

Надеюсь, это поможет

1 голос
/ 29 августа 2011

Я не уверен, связано ли это с вашей проблемой.

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

public void cleanUpMemory(){
    try {
        Field fMapInView = MapView.class.getDeclaredField("mMap");
        AccessibleObject.setAccessible(new AccessibleObject[]{fMapInView}, true);
        fMapInView.set(this, null);
    } catch (Exception e) {
        e.printStackTrace();
    }

    try {
        Field fConverterInView = MapView.class.getDeclaredField("mConverter");
        AccessibleObject.setAccessible(new AccessibleObject[]{fConverterInView}, true);
        fConverterInView.set(this, null);
    } catch (Exception e) {
        e.printStackTrace();
    }

    try {
        Field fControllerInView = MapView.class.getDeclaredField("mController");
        AccessibleObject.setAccessible(new AccessibleObject[]{fControllerInView}, true);
        fControllerInView.set(this, null);
    } catch (Exception e) {
        e.printStackTrace();
    }

    try {
        Field fZoomHelperInView = MapView.class.getDeclaredField("mZoomHelper");
        AccessibleObject.setAccessible(new AccessibleObject[]{fZoomHelperInView}, true);
        fZoomHelperInView.set(this, null);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
...