2012-11-15 29 views
1

可能重複:
Android Out of Memory error with Lazy Load images
Outofmemory error during scrolling the listview fastly如何在列表視圖滾動期間解決outofmemory錯誤?

我使用列表視圖來顯示圖像,從Facebook的api.I名稱可以能夠顯示圖像correctly.My問題是,當我快速滾動列表我得到

11-15 18:41:29.841: E/dalvikvm-heap(32303): 786432-byte external allocation too large for this process. 
11-15 18:41:29.887: I/dalvikvm-heap(32303): Clamp target GC heap from 32.095MB to 32.000MB 
11-15 18:41:29.887: E/GraphicsJNI(32303): VM won't let us allocate 786432 bytes 
11-15 18:41:29.887: D/dalvikvm(32303): GC_FOR_MALLOC freed 0K, 51% free 4380K/8903K, external 23826K/25864K, paused 31ms 
11-15 18:41:29.887: D/skia(32303): --- decoder->decode returned false 
11-15 18:41:29.887: W/dalvikvm(32303): threadid=18: thread exiting with uncaught exception (group=0x40018560) 
11-15 18:41:29.887: E/AndroidRuntime(32303): FATAL EXCEPTION: Thread-29 
11-15 18:41:29.887: E/AndroidRuntime(32303): java.lang.OutOfMemoryError: bitmap size exceeds VM budget 
11-15 18:41:29.887: E/AndroidRuntime(32303): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 
11-15 18:41:29.887: E/AndroidRuntime(32303): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470) 
11-15 18:41:29.887: E/AndroidRuntime(32303): at com.beerbro.utils.ImageLoader.decodeFile(ImageLoader.java:156) 
11-15 18:41:29.887: E/AndroidRuntime(32303): at com.beerbro.utils.ImageLoader.getBitmap(ImageLoader.java:99) 
11-15 18:41:29.887: E/AndroidRuntime(32303): at com.beerbro.utils.ImageLoader$PhotosLoader.run(ImageLoader.java:214) 

但是當我慢慢地滾動這個列表視圖我沒有收到任何錯誤。

這裏是我的Adapter類,我調用Imageloader類的displayImage方法將我的url轉換爲位圖並將其設置爲imageview。

public class MySimpleArrayAdapter extends ArrayAdapter<String> { 
    private Activity context; 
    ArrayList<String> namear,msgar,idar,profimage,postimage,commentsnum,objectid,urlString; 

    ImageLoader imageLoader; 
    Bitmap[] bitdata; 


    public MySimpleArrayAdapter(Activity c,int i,ArrayList<String> postpic, ArrayList<String> names,ArrayList<String> msg,ArrayList<String> id,ArrayList<String> proimg,Bitmap[] bit,ArrayList<String> comment,ArrayList<String> objid,ArrayList<String> web) { 

     super(c, i, names); 
    Log.e("adapter","adap"); 
    this.context = c; 
    this.namear = names; 
    this.msgar = msg; 
    this.idar = id; 
    this.profimage=proimg; 
    this.postimage=postpic; 
    this.bitdata=bit; 
    this.commentsnum=comment; 
    this.objectid=objid; 
    this.urlString=web; 
    this.imageLoader = new ImageLoader(context); 
    } 


    @Override 
    public View getView(final int position, View convertView, ViewGroup parent) { 
     View rowView=convertView ; 
     LayoutInflater inflator = getLayoutInflater(); 
     rowView = inflator.inflate(R.layout.walldata, null); 

     TextView name1 = (TextView) rowView.findViewById(R.id.name); 
     TextView message1 = (TextView) rowView.findViewById(R.id.msg); 
     ImageView profimg= (ImageView) rowView.findViewById(R.id.profile_pic); 
     ImageView postimg= (ImageView) rowView.findViewById(R.id.picpost); 
    TextView comments = (TextView) rowView.findViewById(R.id.comment); 

    Log.e("user",idar.get(position)); 


    Log.e("adapter","adap"); 
    name1.setText(namear.get(position)); 
    if(msgar.get(position)!=""){ 
    message1.setText(msgar.get(position)); 

    } 
    else 
    { 
     message1.setVisibility(View.GONE); 
    } 
    if(!postimage.get(position).equals("")) 
    {try{ 
     imageLoader.DisplayImage(postimage.get(position).replace(" ", "%20"), postimg) ; 

    } 
    catch(Exception e){ 
     e.printStackTrace(); 
    } 
    } 
    else 
    { 
     postimg.setVisibility(View.GONE); 
    } 
    try{ 
     imageLoader.DisplayImage(profimage.get(position).replace(" ", "%20"), profimg) ; 

    } 
    catch(Exception e){ 
     e.printStackTrace(); 
    } 

     comments.setText(commentsnum.get(position)+"\t"+"Comments"); 
     comments.setOnClickListener(new OnClickListener() { 


     public void onClick(View v) { 
      Intent myintent=new Intent(Wall.this,Comments.class); 
      myintent.putExtra("name", namear.get(position)); 
      myintent.putExtra("profimg", profimage.get(position)); 
      myintent.putExtra("message", msgar.get(position)); 
      myintent.putExtra("postpic", postimage.get(position)); 
      myintent.putExtra("objectid", objectid.get(position)); 
      myintent.putExtra("commentsnum",commentsnum.get(position)); 
      myintent.putExtra("webservice", urlString.get(position)); 
      startActivity(myintent); 

     } 
    }); 

    return rowView; 
    } 

這裏是我ImageLoader的類

public class ImageLoader { 

    MemoryCache memoryCache=new MemoryCache(); 
    FileCache fileCache; 
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>()); 

    public ImageLoader(Context context){ 

     photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1); 

     fileCache=new FileCache(context); 
    } 

    public void DisplayImage(String url,ImageView imageView) 
    { 
     imageViews.put(imageView, url); 


     Bitmap bitmap=memoryCache.get(url); 

     if(bitmap!=null) 
     { 
      imageView.setBackgroundDrawable(new BitmapDrawable(bitmap)); 

      System.gc(); 
     } 
     else 
     { 
      queuePhoto(url, imageView); 

     }  
    } 

    private Bitmap createScaledBitmap(Bitmap bitmap, int i, int j, int k) { 
     return null; 
    } 

    private void queuePhoto(String url, 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 Bitmap getBitmap(String url) 
    { 
     File f=fileCache.getFile(url); 

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

     //from web 
     try { 
      Bitmap bitmap=null; 
      URL imageUrl = new URL(url); 
      HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection(); 
      conn.setConnectTimeout(30000); 
      conn.setReadTimeout(30000); 
      InputStream is=conn.getInputStream(); 
      OutputStream os = new FileOutputStream(f); 
      Utils.CopyStream(is, os); 
      os.close(); 
      bitmap = decodeFile(f); 
      return bitmap; 
     } catch (Exception ex){ 
      ex.printStackTrace(); 
      return null; 
     } 
    }//Lalit 

    //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=Wall.width; 
      final int REQUIRED_SIZE1=Wall.height; 

      int width_tmp=o.outWidth, height_tmp=o.outHeight; 
      int scale=1; 

      while(true){ 
       if((width_tmp/2<REQUIRED_SIZE)&&(height_tmp/2<REQUIRED_SIZE1)) 
        break; 
       width_tmp/=2; 
       height_tmp/=2; 
       scale*=2; 
      } 

      //decode with inSampleSize 
      BitmapFactory.Options o2 = new BitmapFactory.Options(); 
      o2.inJustDecodeBounds = true; 
      BitmapFactory.decodeStream(new FileInputStream(f), null, o2); 
      o2.inSampleSize=scale; 
      o2.inJustDecodeBounds = false; 

      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); 
         memoryCache.put(photoToLoad.url, bmp); 
         String tag=imageViews.get(photoToLoad.imageView); 
         if(tag!=null && tag.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) 
       imageView.setBackgroundDrawable(new BitmapDrawable(bitmap)); 

     } 
    } 

    public void clearCache() { 
     memoryCache.clear(); 
     fileCache.clear(); 
    } 
    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels,int width,int height) { 

     Bitmap output = Bitmap.createBitmap(width,height, Config.ARGB_8888); 
     Canvas canvas = new Canvas(output); 

     final int color = 0xff424242; 
     final Paint paint = new Paint(); 
     final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 
     final RectF rectF = new RectF(rect); 
     final float roundPx = pixels; 

     paint.setAntiAlias(true); 
     canvas.drawARGB(0, 0, 0, 0); 
     paint.setColor(color); 
     canvas.drawRoundRect(rectF, roundPx, roundPx, paint); 

     paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 
     canvas.drawBitmap(bitmap, rect, rect, paint); 

     return output; 
    } 

我縮放高達設備屏幕尺寸,即480 * 854的圖像,可以在ImageLoader的類找到這個,我也提到這裏容易理解

 final int REQUIRED_SIZE=Wall.width; 
      final int REQUIRED_SIZE1=Wall.height; 

      int width_tmp=o.outWidth, height_tmp=o.outHeight; 
      int scale=1; 

      while(true){ 
       if((width_tmp/2<REQUIRED_SIZE)&&(height_tmp/2<REQUIRED_SIZE1)) 
        break; 
       width_tmp/=2; 
       height_tmp/=2; 
       scale*=2; 
      } 
    } 

通過查看錯誤我可以理解,內存不足以加載位圖,我不知道如何在運行時釋放內存。 請幫我我介紹了很多在stackoverflow和其他人的鏈接,但無法解決這個問題。

+1

因此,你第四次問同樣的問題,你希望這次得到不同的結果? http://www.brainyquote.com/quotes/quotes/a/alberteins133991。如果你不能很好地回答你的問題,你最後得到了很好的建議。請閱讀它。 – Simon

+0

請提供代碼 –

+0

hello hannes,我提供了最大代碼我可以。請問我你是否不明白任何事情。請現在討論問題 – veerendra

回答

1

好吧,我可以想到的一個解決方法是使用onScrollListener,並向偵聽器設置的適配器添加一個標誌。

當列表正在滾動(flag = true)時,適配器會跳過加載圖像。當列表停止滾動時(標誌= false)適配器加載圖像。您可以通過在適配器上調用notifyDataSetChanged()來觸發圖像加載。

如果我們調用適配器isScrolling的標誌,你的聽衆可能是這樣的:

listView.setOnScrollListener(new OnScrollListener() { 
    public void onScroll(AbsListView view, int firstVisible, 
    int visibleCount, int total) { 
     public void onScrollStateChanged(AbsListView view, int scrollState) { 
      if (scrollState != 0) 
       listView.getAdapter()).isScrolling = true; 
      else { 
       listView.getAdapter().isScrolling = false; 
       listView.getAdapter().notifyDataSetChanged(); 
      } 
     } 
    }); 

而在你的適配器簡單地環繞你的圖像加載代碼這樣的:

if (!isScrolling) { 
    // code to load images 
} 
+0

當我嘗試像上面所說的放置時,出現多個標記錯誤。 – veerendra

+0

listView.getAdapter()中沒有isScrolling isScrolling ... –

+0

有擴展適配器並將其添加到...時 – Barak

0

我不是很確定,但初始圖像的分量

BitmapDrawable bd = (BitmapDrawable) imgView.getDrawable(); 
      if (bd != null) 
      { 
       bd.getBitmap().recycle(); 
       imgView.setImageBitmap(null); 
      } 
+0

if(bitmap!= null)後添加了bitmap.recycle() imageView.setBackgroundDrawable(new BitmapDrawable(bitmap)); System.gc(); bitmap.recycle(); // bitmap = null; // imageView.setImageBitmap(getRoundedCornerBitmap(bitmap,10,70,70)); // imageView.setImageBitmap(bitmap); // Log.v(「first」,「first」); } – veerendra

+0

它沒有爲我工作 – veerendra

+0

莫哈末打招呼,沒u有關於這個問題 – veerendra

1

可能有兩種原因與problems.first之後添加這些行,你不要重複使用已經創建的列表視圖項的istance。我看到你的代碼,爲什麼你注意到代碼。當你滾動列表視圖的速度太快時,vm將無法立即創建足夠快的速度,因此內存不足。並且,最好使用緩衝區。 第二,你的位圖太大,當你滾動得太快時,vm不能代表壓力。要解決這些問題,可以減小位圖的大小。 希望我的answear能爲你做點事情。

+0

我試圖通過保持if(convertview == null){在這裏取所有實例}來重複使用它們,但它首先顯示prevoously加載的圖像,並且需要時間來加載實際的圖像。並且爲了永久滾動,我做的圖像是改變,當我停下來是需要時間,然後設置圖像 – veerendra

+0

這必須是第二個原因,你的位圖太大,你必須處理它。使位圖更小 – honeypig

相關問題