2013-02-18 107 views
4

我在我的應用程序中觸發GC多次並導致性能問題的內存泄漏。我使用MAT生成了leak suspect report。下面是報告:位圖內存泄漏 - Android

問題可疑1: 「android.graphics.Bitmap」 的 一個實例由加載 「」 佔用4194368(20.13%)字節。內存在由「」加載的「byte []」的一個實例中累積。

問題可疑2: 類 「android.content.res.Resources」,由 「」 裝,佔據3962504(19.02%)字節。內存在由「」加載的「java.lang.Object []」的一個實例中累積。

問題可疑3:由裝 「android.graphics.Bitmap」 的 一個實例 「」 佔用3145792(15.10%)字節。內存在由「」加載的「byte []」的一個實例中累積。

從報告來看,很明顯內存泄漏是由於位圖造成的。我研究了很多,但無法糾正這種泄漏。請幫助我。我正在使用ImageLoader類來下載和顯示位圖。要使用這個類,我只需調用displayImage()方法。這裏是代碼:

public class ImageLoader { 

    private static ImageLoader imageLoader; 
    private int maxNoOfConnections = 4; 
    FileCache fileCache; 
    ExecutorService executorService; 
    HttpURLConnection conn; 
    InputStream is; 
    OutputStream os; 
    PhotosLoader photosLoader; 
    Handler handler; 
    Bitmap bitmap; 

    private ImageLoader(Context context) { 
     fileCache = new FileCache(context); 
     executorService = Executors.newFixedThreadPool(maxNoOfConnections); 
     handler = new Handler(); 
    } 

    public static ImageLoader getInstance(Context context) { 
     if (imageLoader == null) 
      imageLoader = new ImageLoader(context); 
     return imageLoader; 
    } 

    public void displayImage(String url, ProgressBar pBar, ImageView imageView) { 
     photosLoader = new PhotosLoader(url, imageView, pBar); 
     executorService.submit(photosLoader); 
    } 

    private Bitmap getBitmap(String url) { 
     File f = fileCache.getFile(url); 

     bitmap = decodeFile(f); 
     if (bitmap != null) 
      return bitmap; 

     try 
     { 
      URL imageUrl = new URL(url); 
      conn = (HttpURLConnection) imageUrl.openConnection(); 
      conn.setConnectTimeout(30000); 
      conn.setReadTimeout(30000); 
      conn.setInstanceFollowRedirects(true); 
      is = conn.getInputStream(); 
      os = new FileOutputStream(f); 
      Utils.CopyStream(is, os); 
      os.close(); 
      bitmap = decodeFile(f); 
      return bitmap; 
     } catch (Exception ex) 
     { 
      Log.e("inNews", "Image Url Malformed"); 
      return null; 
     } 
    } 

    private Bitmap decodeFile(File f) { 
     try 
     { 
      BitmapFactory.Options o = new BitmapFactory.Options(); 
      o.inJustDecodeBounds = true; 
      BitmapFactory.decodeStream(new FileInputStream(f), null, o); 

      final int REQUIRED_SIZE = 70; 
      int width_tmp = o.outWidth, height_tmp = o.outHeight; 
      int scale = 1; 
      while (true) 
      { 
       if (width_tmp/2 < REQUIRED_SIZE || height_tmp/2 < REQUIRED_SIZE) 
        break; 
       width_tmp /= 2; 
       height_tmp /= 2; 
       scale *= 2; 
      } 

      BitmapFactory.Options o2 = new BitmapFactory.Options(); 
      o2.inSampleSize = scale; 
      return BitmapFactory.decodeStream(new FileInputStream(f), null, o2); 
     } catch (FileNotFoundException e) 
     { 
     } 
     return null; 
    } 

    class PhotosLoader implements Runnable { 
     String url; 
     ImageView imageView; 
     ProgressBar pBar; 
     Bitmap bmp; 

     public PhotosLoader(String url, ImageView imageView, ProgressBar pBar) { 
      this.url = url; 
      this.imageView = imageView; 
      this.pBar = pBar; 
     } 

     @Override 
     public void run() { 
      bmp = getBitmap(url); 
      handler.post(new Runnable() { 
       @Override 
       public void run() { 
        if (bmp != null) 
        { 
         pBar.setVisibility(View.GONE); 
         imageView.setImageBitmap(bmp); 
        } else 
        { 
         pBar.setVisibility(View.GONE); 
         imageView.setImageResource(R.drawable.img_no_image_grid); 
        } 
       } 
      }); 
     } 
    } 

} 

請幫我糾正我的代碼。謝謝!

說明:我沒有使用bitmap.recycle(),因爲文檔中提到後Honeycomb GC收集位圖並且不再需要強制回收它!

+0

當你旋轉設備時,隨機時間總是在同一時間收到內存泄漏......? – Fustigador 2013-02-18 13:14:42

+0

您應該閱讀有關Android文檔中的SoftReference,在您的情況下,如果您將返回的位圖存儲在某處,則只要執行涉及加載圖像的操作,它就會保留在內存 – 2013-02-18 13:14:48

+0

@Fustigador中。沒有專門旋轉設備。 – gauravsapiens 2013-02-18 13:22:24

回答

0

內存泄漏問題始終是Java的問題。 我瞭解你的代碼,代碼簡單的imagecache工具。檢查SampleSize值和執行程序服務僅在一個線程上運行。四線程有大的內存和這個後臺線程動作。您的「處理程序」交換到「runOnUIThread」

您應該使用;

活動活動 =(活動)imageView.getContext();

__activity__.runOnUIThread(new Runnable() 
{ 
if (bmp != null) 
        { 
         pBar.setVisibility(View.GONE); 
         imageView.setImageBitmap(bmp); 
        } else 
        { 
         pBar.setVisibility(View.GONE); 
         imageView.setImageResource(R.drawable.img_no_image_grid); 
        } 
}); 
+0

我已經嘗試過,但它並沒有幫助,而是可能不必參考「活動」。 – gauravsapiens 2013-02-18 13:59:46

+0

我使用與您的代碼相同的imagecache,但沒有發生任何內存泄漏錯誤。如果您想要,我會在2-3小時後發送我的代碼。你給我通信鏈接,如「電子郵件」我會發送java文件組。(包) – nurisezgin 2013-02-18 14:50:40

+0

是的,我的郵件ID是g.[email protected]。謝謝 ! :) – gauravsapiens 2013-02-19 05:51:28

0

我認爲這個問題是Singleton實例......我做了LazyList項目的一個分支,檢查:

https://github.com/nicolasjafelle/LazyList

我有同樣的內存泄漏,但是,也許我我錯了,這個單例永遠不會被垃圾收集,除非你用System.exit()來殺死這個進程。

這就是爲什麼原始LazyList項目不使用Singleton。我也認爲,如果你需要一個緩存,你將需要快速和相同的所有應用程序。

當您調用clearCache收集的位圖時,此ImageLoader中的重要事情是FileCache和MemoryCache。

0

我認爲答案很簡單,只要在不需要內存/ OOM異常出現時繼續清除緩存即可。我爲你做了這件事

MemoryCache memoryCache = new MemoryCache(); 

try { 
     Bitmap bitmap = null; 
     URL imageUrl = new URL(url); 
     HttpURLConnection conn = (HttpURLConnection) imageUrl 
       .openConnection(); 
     conn.setConnectTimeout(30000); 
     conn.setReadTimeout(30000); 
     conn.setInstanceFollowRedirects(true); 
     InputStream is = conn.getInputStream(); 
     OutputStream os = new FileOutputStream(f); 
     Utils.CopyStream(is, os); 
     os.close(); 
     conn.disconnect(); 
     bitmap = decodeFile(f); 
     return bitmap; 
    } catch (Throwable ex) { 
     ex.printStackTrace(); 
     if (ex instanceof OutOfMemoryError) 
      memoryCache.clear(); 
     return null; 
    }