2012-08-22 109 views
6

正如標題所說,我有加載佈局拋出的內存不足異常。有人會認爲這是一個內存泄漏問題,但經過兩天的爭鬥後,我不再確定了。該應用程序有10多項活動,其中大部分都有背景圖片。OutOfMemoryError創建位圖

幾個事實/發現我做:

  • 到目前爲止,問題只在Galaxy Nexus的出現運行Android 4.0.3。我無法在Nexus S(4.1.1)和Galaxy S II(2.3.3)上重現它。

  • 屏幕方向不會改變。事實上,我的大部分活動無論如何都鎖定在肖像上。

  • 只是爲了笑我在打開新的活動時添加了對finish()的呼叫,所以當時內存中不會有多於一個的活動。驗證了onDestroy正在被調用。

定義爲:

@Override 
public void onDestroy() 
{ 
    super.onDestroy(); 
    cleanupDrawables(contentView); 

    // null all the fields referencing views and drawables 

    System.gc(); 
} 

其中cleanupDrawables()是:

protected static void cleanupDrawables(View view) 
{ 
    cleanupDrawable(view.getBackground()); 

    if (view instanceof ImageView) 
     cleanupDrawable(((ImageView)view).getDrawable()); 
    else if (view instanceof TextView) 
    { 
     TextView tv = (TextView)view; 
     Drawable[] compounds = tv.getCompoundDrawables(); 
     for (int i = 0; i < compounds.length; i++) 
      cleanupDrawable(compounds[i]); 
    } 
    else if (view instanceof ViewGroup && !(view instanceof AdapterView)) 
    { 
     ViewGroup vg = (ViewGroup)view; 
     for (int i = 0; i < vg.getChildCount(); i++) 
      cleanupDrawables(vg.getChildAt(i)); 
     vg.removeAllViews(); 
    } 
} 

protected static void cleanupDrawable(Drawable d) 
{ 
    if (d == null) 
     return; 

    d.setCallback(null); 

    if (d instanceof BitmapDrawable) 
     ((BitmapDrawable)d).getBitmap().recycle(); 
    else if (d instanceof LayerDrawable) 
    { 
     LayerDrawable layers = (LayerDrawable)d; 
     for (int i = 0; i < layers.getNumberOfLayers(); i++) 
      cleanupDrawable(layers.getDrawable(i)); 
    } 
} 
  • 看着Eclipse的堆檢查,內存似乎是穩定的,即一些活動需要更多的內存比其他人多,但是它在關閉時被釋放,並且隨着時間的推移它看起來很穩定。

  • 根據SO圖像上的相關答案存儲在本機內存中,但是this老兄聲稱他們應該在Android 3以後堆上,所以如果這確實是圖像內存泄漏,我應該看到內存增加。

我努力的最終結果是,我仍然發現內存不足錯誤,但速度不如以前那麼快。在發生錯誤之前,我開始看到可見的位圖損壞,在添加cleanupDrawables()代碼之前,情況並非如此。我推斷,致電Bitmap.recycle()正在導致腐敗,儘管此代碼僅在onDestroy上被調用。兩個位圖都出現損壞,這些位圖都是出現在許多活動中的常見樣式的一部分,以及僅在一個活動中顯示的位圖。

在我調查的結果短是相當不確定的。在這一點上,我不知道還有什麼要嘗試。

參考錯誤堆棧跟蹤:

08-22 10:49:51.889: E/AndroidRuntime(31697): java.lang.RuntimeException: Unable to start activity ComponentInfo{klick.beatbleeds/klick.beatbleeds.Bleeds}: android.view.InflateException: Binary XML file line #67: Error inflating class <unknown> 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1955) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1980) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.access$600(ActivityThread.java:122) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1146) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.os.Handler.dispatchMessage(Handler.java:99) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.os.Looper.loop(Looper.java:137) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.main(ActivityThread.java:4340) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Method.invokeNative(Native Method) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Method.invoke(Method.java:511) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at dalvik.system.NativeStart.main(Native Method) 
08-22 10:49:51.889: E/AndroidRuntime(31697): Caused by: android.view.InflateException: Binary XML file line #67: Error inflating class <unknown> 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.createView(LayoutInflater.java:606) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.onCreateView(LayoutInflater.java:653) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:678) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.rInflate(LayoutInflater.java:739) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.inflate(LayoutInflater.java:489) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.inflate(LayoutInflater.java:396) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.inflate(LayoutInflater.java:352) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at klick.beatbleeds.ActivityBase.setContentView(ActivityBase.java:82) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at klick.beatbleeds.Bleeds.onCreate(Bleeds.java:40) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.Activity.performCreate(Activity.java:4465) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1919) 
08-22 10:49:51.889: E/AndroidRuntime(31697): ... 11 more 
08-22 10:49:51.889: E/AndroidRuntime(31697): Caused by: java.lang.reflect.InvocationTargetException 
08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Constructor.constructNative(Native Method) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Constructor.newInstance(Constructor.java:417) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.createView(LayoutInflater.java:586) 
08-22 10:49:51.889: E/AndroidRuntime(31697): ... 23 more 
08-22 10:49:51.889: E/AndroidRuntime(31697): Caused by: java.lang.OutOfMemoryError 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.nativeCreate(Native Method) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.createBitmap(Bitmap.java:605) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.createBitmap(Bitmap.java:551) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.content.res.Resources.loadDrawable(Resources.java:1937) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.content.res.TypedArray.getDrawable(TypedArray.java:601) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.View.<init>(View.java:2780) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.ViewGroup.<init>(ViewGroup.java:385) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.widget.LinearLayout.<init>(LinearLayout.java:174) 
08-22 10:49:51.889: E/AndroidRuntime(31697): at android.widget.LinearLayout.<init>(LinearLayout.java:170) 
08-22 10:49:51.889: E/AndroidRuntime(31697): ... 26 more 

這裏是一個視圖的截圖來了解有多少圖像用在這裏 enter image description here

+0

那麼,這些位圖(分辨率)有多大?如果您用較小的圖像替換它們(僅用於踢腿),問題會消失嗎? – dmon

+0

從視圖中清理位圖資源不是必需的,只會減慢您轉換到新活動的速度。你膨脹什麼樣的佈局以及多少圖像? – DeeV

+0

我也同意@Deev,你不需要手動「清理」你的位圖摧毀。 – dmon

回答

2

使用這些靜態方法來找到的想法出確切的問題(S):

public static void showBitmapSize(Bitmap bitmap) { 
    Log.d("test", "bitmap dimensions: w:" + bitmap.getWidth() + ", h:" + bitmap.getHeight() + " memory: " + (bitmap.getRowBytes() * bitmap.getHeight()/1048576d)); 
} 

而最重要的是:

static double lastavail; 
static double initavail; 
static boolean first = true; 

public static void showMemoryStats() { 
    showMemoryStats(""); 
} 

public static void showMemoryStats(String message) { 
    Log.i("memory", message + "----------------------------------------------------------------------------------------"); 
    double nativeUsage = Debug.getNativeHeapAllocatedSize(); 
    Log.i("memory", "nativeUsage: " + (nativeUsage/1048576d)); 
    //current heap size 
    double heapSize = Runtime.getRuntime().totalMemory(); 
//  Log.i("memory", "heapSize: " + (heapSize/1048576d)); 
    //amount available in heap 
    double heapRemaining = Runtime.getRuntime().freeMemory(); 
//  Log.i("memory", "heapRemaining: " + (heapRemaining/1048576d)); 
    double memoryAvailable = Runtime.getRuntime().maxMemory() - (heapSize - heapRemaining) - nativeUsage; 
    Log.i("memory", "memoryAvailable: " + (memoryAvailable/1048576d)); 

    if (first) { 
     initavail = memoryAvailable; 
     first = false; 
    } 
    if (lastavail > 0) { 
     Log.i("memory", "consumed since last: " + ((lastavail - memoryAvailable)/1048576d)); 
    } 
    Log.i("memory", "consumed total: " + ((initavail - memoryAvailable)/1048576d)); 

    lastavail = memoryAvailable; 

    Log.i("memory", "-----------------------------------------------------------------------------------------------"); 
} 

除以1048576只能得到以MB爲單位的值(至少對於我來說更容易以MB爲單位)。

撥打電話showMemoryStats,並在setContentView()呼叫之前發送了一些有意義的消息,之後再發送一個消息。當你開始一個新的活動等等。最後你會發現你的問題的確切原因。

手動回收可能是必要的。我必須在我的應用程序的某些地方實現它。還使用了一個位圖密集型(許多背景和圖片),並在所有設備中都存在這種問題。通過這些方法,我能夠找到所有問題並進行合理處理。

啊。而且您的問題可能有一個快速解決方案,您說它只出現在Galaxy Nexus中。這是你提到的唯一的xhdpi設備。 您可能只有在drawable或drawable-hdpi文件夾中才有所有位圖。 xhdpi設備將從drawable或drawable-hdpi獲取位圖並放大(儘管它們可能已經處於正確的大小),並且這會消耗大量內存。解決方案:創建drawable-xhdpi文件夾(如果不存在),並將位圖副本放在那裏。