2

可能重复:
Android Out of Memory error with Lazy Load images
Outofmemory error during快速滚动列表视图

我正在使用 listview 显示图像,来自 facebook api 的名称。我可以正确显示图像。我的问题是当我快速滚动列表时我得到

    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)

但是当我慢慢滚动这个列表视图时,我没有收到任何错误。

这是我的适配器类,我在其中调用 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和其他人中的许多链接,但无法解决这个问题。

4

3 回答 3

1

好吧,我能想到的解决问题的一种方法是使用onScrollListener, 并向适配器添加一个可由侦听器设置的标志。

当列表滚动时(标志 = 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
}
于 2012-11-15T16:32:28.503 回答
1

您的问题可能有两个原因。首先,您没有重用已创建的列表视图项目的距离。我看到你的代码,为什么你记下这些代码。当您滚动列表视图太快时,虚拟机将无法足够快地创建即时,因此内存不足。而且,您最好使用缓冲区。第二,你的bitmap太大,当你滚动的太快时,vm承受不住压力。解决这些,你可以减小bitmap的大小。希望我的回答可以为你做点什么。

于 2012-11-15T13:56:13.423 回答
0

我不太确定,但在初始化图像组件后添加这些行

    BitmapDrawable bd = (BitmapDrawable) imgView.getDrawable();
            if (bd != null)
            {
                bd.getBitmap().recycle();
                imgView.setImageBitmap(null);
            }
于 2012-11-15T13:29:16.037 回答