2011-08-21 38 views
0

我從用戶那裏收到了不少OutOfMemoryError報告,每個報告都來自同一個包含MapView的Activity。我認爲這是一個孤立的例外,在我的應用程序中只有這一個地方,我無法弄清楚問題所在。任何人都可以給我一些關於爲何發生這種情況的指示?Android - 許多OutOfMemoryError異常僅在單個Activity中使用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 
+0

您是否試圖通過轉儲hprof來分析內存使用情況,並使用內存分析器(如Eclipse Memory Analyzer)分析它?這應該讓你瞭解最大消費的位置,並讓你開始尋找什麼。 – momo

+0

嘗試移動行: 位圖bmp = BitmapFactory.decodeResource(mContext.getResources(),resourceId); out of draw()方法,以便每次調用draw()(它通常是*)時不會重新創建位圖。 – tonys

+0

我會嘗試兩種建議並回報。 – hooked82

回答

7

我下載的應用程序,同時觀察通過dumpsys內存用它玩。

事情看起來很正常,內存每次都會被回收。我無法再現這種情況,但我確實看到可能與此有關的一個峯值。無論何時在衛星上移動地圖或縮放(基本刷新瓷磚)時,內存都會出現短暫的高峯。如果你做得夠快,你不會給它一個回收的機會,它會上升。

現在,我的手機是Android 3.3.4,配置相當不錯,所以也許GC效率更高。我想知道,如果我的老式測試手機能夠較慢地回收內存,並且因此當我到達地圖時(比如說添加魚之後),我仍然可以獲得以前未被GC回收的活動的內存。然後,我會做什麼,我會去我的位置,並通過放大/縮小查看事情。通過以前活動的先前內存加起來可能會使手機達到極限。

這只是一個理論,雖然我在路上,並沒有獲得我所有的測試手機。你知道哪些版本的手機會崩潰嗎?我會在3-4天后回來,我可以在舊手機上試用這款應用。

已更新: 我已經在這個應用程序上運行了更多的實驗。我幾乎可以肯定,連續添加魚類會增加更多的記憶。我不斷添加和刪除魚,並檢查了內存繼續通過dumpsys meminfo。專業版的真正用戶,甚至是不斷添加和刪除魚的簡體中文用戶最終可能會接近極限,之後轉到地圖將觸發外存錯誤,因爲有內存跳轉到地圖中。這裏是快照後,我並移除魚類幾次

 
** 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 

你的私有內存跳到共有37948我確信如果我繼續添加和刪除魚類,它會拋出OutOfMemoryException異常最終

更新(幾分鐘後): 我使用上述理論設法使應用程序崩潰。我必須在發生之前多次添加和移除魚。應用程序崩潰之前,它可能會超過50條魚。

我的猜測是SQL無法正確清理。每一組中添加和刪除10種魚類(這是精簡版的版本的限制)後,望着dumpsys,我看到

 
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的時候總的內存大約是58MB(這可能不同於電話到手機)。對於一個參考,這裏是一個類似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) 

希望它可以幫助

+0

哇,謝謝這樣的詳細研究。我沒有想到這種情況。我也會嘗試讓我的手也放在一些較舊的測試手機上。至於遇到問題的模型,谷歌給了一些很好的信息:「Droid」和「其他」。 *嘆*期待您的發現,謝謝! – hooked82

+3

+1下載應用程序並進行測試!哇!!沒有多少人會爲了幫助別人而走出困境。 – bluefalcon

+0

@ hooked82看起來地圖只是觸發器,但添加和移除魚類確實會增加內存。看起來,即使在精簡版版本中,添加和刪除魚類也不斷增加內存,而在專業版中,您甚至可能會更快地獲得內存。讓我繼續添加和刪除這裏的魚多次,看看它是否觸發OutOfMemoryException – momo

1

我不知道這是否與你的問題。

link討論了一個memory leak issue和一個建議的解決方案。基於此我在使用此版本我擴展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(); 
    } 
} 
+0

這可能是導致問題的原因。我會仔細研究一下,一旦我可以確認/拒絕修復,就會報告回來。感謝您的鏈接! – hooked82

+0

@Samuel,你叫什麼「適當的地方」? –

+0

根據框架[onDestroy](https://code.google.com/p/android/issues/detail?id=2181#c11)的開發者的討論鏈接是海報,他們。但我想你的里程可能會有所不同。 – Samuel