2011-11-15 10 views
0
java.lang.OutOfMemoryError: bitmap size exceeds VM budget. 

このimageloaderクラスを使用してインターネットからgridviewに画像を更新しています。私は約1000枚の画像をダウンロードする必要がありますが、同じエラーが発生します。 同じように私を助けてください。「ビットマップサイズがVM予算を超えています」エラーを解決する方法はありますか?

public class ImageLoader { 


    public static HashMap<String, Bitmap> cache=new HashMap<String, Bitmap>(); 

    private File cacheDir; 
    int i=0; 
    File f; 
    BitmapDrawable b; 
    public ImageLoader(Context context){ 
     //Make the background thead low priority. This way it will not affect the UI performance 
     photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1); 

     GridViewConfig.clearFilelist(); 
     GridViewConfig.file_list=new ArrayList<String>(); 


     //Find the dir to save cached images 
     if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) 
      cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList"); 
     else 
      cacheDir=context.getCacheDir(); 
     if(!cacheDir.exists()) 
      cacheDir.mkdirs(); 
    } 

    final int stub_id=R.drawable.imagelistback_106x133; 
    public void DisplayImage(String url, Activity activity, ImageView imageView) 
    { 

     if(cache.containsKey(url)) 
     { 

      /* if(Common.isgallery) 
      { 
      b=null; 
      b=new BitmapDrawable(cache.get(url)); 
      Bitmap bb=Bitmap.createScaledBitmap(cache.get(url), 300, 300, true); 
      imageView.setImageBitmap(bb); 
      bb.recycle(); 
      } 
      else 
      {*/ 

       imageView.setImageBitmap(cache.get(url)); 
      /* }*/ 
     } 
     else 
     { 
      //Toast.makeText(activity, "ELSE in dISPLAY iMAGES", Toast.LENGTH_LONG).show(); 
      queuePhoto(url, activity, imageView); 
      imageView.setImageResource(stub_id); 
     } 



    } 

    private void queuePhoto(String url, Activity activity, ImageView imageView) 
    { 
     //This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them. 
     photosQueue.Clean(imageView); 
     PhotoToLoad p=new PhotoToLoad(url, imageView); 
     synchronized(photosQueue.photosToLoad){ 
      photosQueue.photosToLoad.push(p); 
      photosQueue.photosToLoad.notifyAll(); 
     } 

     //start thread if it's not started yet 
     if(photoLoaderThread.getState()==Thread.State.NEW) 
      photoLoaderThread.start(); 
    } 
    public void Createfile(String url) 
    { 
     String filename=String.valueOf(url.hashCode()); 
     f=new File(cacheDir, filename); 
     GridViewConfig.file_list.add(f.getAbsolutePath()); 
    } 
    public Bitmap getBitmap(String url) 
    { 

     //I identify images by hashcode. Not a perfect solution, good for the demo. 
     String filename=String.valueOf(url.hashCode()); 
     //String filename=GridViewConfig.sku_list.get(i); 
     File f=new File(cacheDir, filename); 
     GridViewConfig.file_list.add(f.getAbsolutePath()); 
     /*if(Common.firstLoading) 
     { 
     GridViewConfig.file_list.add(f.getAbsolutePath()); 
     i++; 
     Log.v("gooooooooogle file list", "file list size "+i); 

     }*/ 
     //from SD cache 
     Bitmap b = decodeFile(f); 
     if(b!=null) 
      return b; 

     //from web 
     try { 
      Bitmap bitmap=null; 
      InputStream is=new URL(url).openStream(); 
      OutputStream os = new FileOutputStream(f); 
      Utils.CopyStream(is, os); 
      os.close(); 
      bitmap = decodeFile(f); 
      return bitmap; 
     } catch (Exception ex){ 
      ex.printStackTrace(); 
      return null; 
     } 
    } 

    //decodes image and scales it to reduce memory consumption 
    private Bitmap decodeFile(File f){ 
     try { 
      //decode image size 
      BitmapFactory.Options o = new BitmapFactory.Options(); 
      o.inJustDecodeBounds = true; 
      BitmapFactory.decodeStream(new FileInputStream(f),null,o); 

      //Find the correct scale value. It should be the power of 2. 
      final int REQUIRED_SIZE=50; 
      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++; 
      } 
      Log.v("", "Scale"+scale); 
      //decode with inSampleSize 
      BitmapFactory.Options o2 = new BitmapFactory.Options(); 
      o2.inSampleSize=scale; 
      return BitmapFactory.decodeStream(new FileInputStream(f), null, o2); 
     } catch (FileNotFoundException e) {} 
     return null; 
    } 

    //Task for the queue 
    private class PhotoToLoad 
    { 
     public String url; 
     public ImageView imageView; 
     public PhotoToLoad(String u, ImageView i){ 
      url=u; 
      imageView=i; 
     } 
    } 

    PhotosQueue photosQueue=new PhotosQueue(); 

    public void stopThread() 
    { 
     photoLoaderThread.interrupt(); 
    } 

    //stores list of photos to download 
    class PhotosQueue 
    { 
     private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>(); 

     //removes all instances of this ImageView 
     public void Clean(ImageView image) 
     { 
      for(int j=0 ;j<photosToLoad.size();){ 
       if(photosToLoad.get(j).imageView==image) 
        photosToLoad.remove(j); 
       else 
        ++j; 
      } 
     } 
    } 

    class PhotosLoader extends Thread { 
     public void run() { 
      try { 
       while(true) 
       { 
        //thread waits until there are any images to load in the queue 
        if(photosQueue.photosToLoad.size()==0) 
         synchronized(photosQueue.photosToLoad){ 
          photosQueue.photosToLoad.wait(); 
         } 
        if(photosQueue.photosToLoad.size()!=0) 
        { 
         PhotoToLoad photoToLoad; 
         synchronized(photosQueue.photosToLoad){ 
          photoToLoad=photosQueue.photosToLoad.pop(); 
         } 
         Bitmap bmp=getBitmap(photoToLoad.url); 
         cache.put(photoToLoad.url, bmp); 
         if(((String)photoToLoad.imageView.getTag()).equals(photoToLoad.url)){ 
          BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView); 
          Activity a=(Activity)photoToLoad.imageView.getContext(); 
          a.runOnUiThread(bd); 
         } 
        } 
        if(Thread.interrupted()) 
         break; 
       } 
      } catch (InterruptedException e) { 
       //allow thread to exit 
      } 
     } 
    } 

    PhotosLoader photoLoaderThread=new PhotosLoader(); 

    //Used to display bitmap in the UI thread 
    class BitmapDisplayer implements Runnable 
    { 
     Bitmap bitmap; 
     ImageView imageView; 
     public BitmapDisplayer(Bitmap b, ImageView i) 
     { 
      bitmap=b; 
      imageView=i; 
     } 
     public void run() 
     { 
      if(bitmap!=null) 
       /*if(Common.isgallery) 
       { 
        b=null; 
        b=new BitmapDrawable(bitmap); 
        Bitmap bit=Bitmap.createScaledBitmap(bitmap, 300, 300, true); 
        imageView.setImageBitmap(bit); 
        bit.recycle(); 
       } 
       else 
       {*/ 
       imageView.setImageBitmap(bitmap); 

       /*}*/ 
      else 
       imageView.setImageResource(stub_id); 
     } 
    } 

    public void clearCache() { 
     //clear memory cache 
     cache.clear(); 

     //clear SD cache 
     File[] files=cacheDir.listFiles(); 
     for(File f:files) 
      f.delete(); 
    } 

} 

答えて

1

私がお勧めします:

  • cacheに代わり、単純なビットマップのSoftReference<Bitmap>またはWeakReference<Bitmap>を使用しています。そうすれば、あなたはOOMに直面することはありません。
  • options for decoding(Awais Tariqが提案したように) - inSampleSizeオプションを使用するとメモリが改善されます(あなたのコードで既に行われているようです)。
  • ダウンロードした画像のSDカードにキャッシュを維持します。その他のオプションについては、this questionを参照してください(既にあなたのコードで行っているようです)。
+0

Sandrstarの提案は優れています。 - OMEが発生する前にGCがこれらのオブジェクトを解放するので、SoftReference のコレクションを選択してください。 - 削除されるアイテムと削除されるアイテムを詳細に制御するLRUキャッシュを見てください。 (本質的にLinkedHashMapを使用します) – jagsaund

0

ビットマップオプションを使用してビットマップを割り当てます。 http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

さらに手動でガベージコレクタを呼び出すようにしようと、アプリケーションが閉じられていない限り、ビットマップメモリ​​がそれまでは解放されていないとしてあなたがビットマップのあまりのオブジェクトを使用しますが、されていません。..

+0

私はsoftreferenceを使用しようとしましたが、まだ同じエラーを変更しません。 – JCJ

0

を(にSystem.gc())作業終了後にそれらを解放します。 bitmapObj.recycle();特定のビットマップオブジェクトで完了したとき。

そのオブジェクトが占有するメモリを解放します。

0

私は顔を持っていると私は、このための恒久的な解決策を持って、同じ問題:

まず、使用しているものは何でも画像あなたはそれが少ないメモリを消費するように、それらを拡張する必要があり、あなたの「OutOfMemoryErrorのは」ということ解決する。

例: 訪問それより少ないメモリを消費するように、あなたが画像を使うべき復号した後、このリンク

http://android-solution-sample.blogspot.com/search/label/Bitmap%20out%20of%20memory

関連する問題