简体   繁体   中英

Android: outOfMemoryError with bitmaps in Gallery

I have an app which downloads images from a server, stores them on the sd card and then displays them to the user as a slideshow. The app has a number of different slideshows that the user can view. My problem is that after I view a few galleries I get an out of memory error. I have done quite a bit of Googling, and have read Romain Guy's Avoiding Memory Leaks article about 20 times. I have tried to replicate his unbindDrawables() method but the but removeAllViews() cannot be called on Gallery objects. I have also tried to call recycle() on all the bitmaps I can but when I do it on the bitmap within the adapter, the app throws an error as soon as I open an image gallery. I have also tried recoding the slideshow so that I create all the bitmaps outside the adapter and then pass them in as an array — this allows me to loop through my bitmap array and call recycle() on each one in the onDestroy method in the slideshow activity — but this actually seems to make the leak worse not better.

Here is the code for my Slideshow activity:

public class Slideshow extends Activity {
    static String galleryId;
    public static final int MSG_DOWNLOADED = 0;
    static  Handler handler;
    static Gallery g;
    static ArrayList<String> filePaths;
    static String subFolder;
    static ArrayList<String>  imageToGet;
    static LinearLayout pb;
    static boolean firstTime = true;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
            setContentView(R.layout.slideshow);
            Context context = getApplicationContext();


            Bundle extras = getIntent().getExtras();

            if(extras != null){
            galleryId = extras.getString("galleryId");
            }
            pb = (LinearLayout) findViewById(R.id.progress); 
             handler = new Handler(){
                    @Override
                    public void handleMessage(Message msg){
                        switch (msg.what){
                        case MSG_DOWNLOADED:
                            g.setAdapter(new SlideshowAdapter(getApplicationContext(), R.id.gallery, filePaths));
                            pb.setVisibility(8);
                            if(firstTime){
                            Thread images = new Thread(){
                                public void run(){
                                    ImageGallery gallery = XMLParser.getGalleryById(galleryId, false, getApplicationContext());
                                    ArrayList<String> imageURLs = gallery.getImageURLs();
                                    String subFolder = "gallery"+galleryId+"/";
                                    getImages(imageURLs, subFolder, false);
                                }
                            };
                            images.start();
                            firstTime = false;
                            }
                            break;
                        }
                    }
                };
            ImageGallery gallery = XMLParser.getGalleryById(galleryId, false, context);
            filePaths = gallery.getFilePaths();
            ArrayList<String> imageURLs = gallery.getImageURLs();
            subFolder = "gallery"+galleryId+"/";
            g = (Gallery) findViewById(R.id.gallery);
            if(filePaths.size() > 0){
            String filePath = filePaths.get(0);
            String url = imageURLs.get(0);
            imageToGet = new ArrayList<String>();
            imageToGet.add(url);

            if(filePaths.size() == 1 ){
                File file = new File(filePath);
                if(!file.exists()){
                    Thread getFirstImage = new Thread(){
                        public void  run(){
                            Log.d("ClubSlideshow getting only image", ""+imageToGet.get(0));
                            getImages(imageToGet, subFolder, false);
                            handler.sendEmptyMessage(MSG_DOWNLOADED);
                        }
                    };
                    getFirstImage.start();
                }

            }else if(filePaths.size()>1){
                String filePath2 = filePaths.get(1);
                String url2 = imageURLs.get(1);
                File file = new File(filePath);
                File file2 = new File(filePath2);
                imageToGet.add(url2);
                if(!file.exists()||!file2.exists()){
                    Thread getFirstImage = new Thread(){
                        public void  run(){
                            getImages(imageToGet, subFolder, false);
                            handler.sendEmptyMessage(MSG_DOWNLOADED);
                        }
                    };
                    getFirstImage.start();
                }

            }
            }


    }

    public void getImages(ArrayList<String> imageURLs, String subFolder, boolean force){
        DataCache.downloadFromUrlArray(imageURLs, subFolder, force, getApplicationContext());
    }

    public class ImageThread implements Runnable{
        public ImageThread(ArrayList<String> imageURLs, String subFolder, Boolean force){
            getImages(imageURLs, subFolder, force);
        }

        public void run(){

        }
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
        g.setAdapter(null);
    }

}

And here is my code for my SlideshowAdapter class:

public class SlideshowAdapter extends ArrayAdapter<String> {
    int mGalleryItemBackground;
    private Context mContext;
    private ArrayList<String> images = new ArrayList<String>();
    Bitmap bMap;
    String filePath;
    File file;
    ImageView i;

    public SlideshowAdapter(Context c, int resourceId, ArrayList<String> objects){
        super(c, resourceId, objects);
        this.mContext = c;
        this.images = objects;
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if(convertView !=null){
            i = (ImageView) convertView;
        }else{
            i = new ImageView(mContext);
        }

        filePath = mContext.getFilesDir()+"/"+images.get(position);

        file = new File(filePath);
        if(file.exists()){
            bMap = BitmapFactory.decodeFile(filePath);
            i.setImageBitmap(bMap);
        }

        i.setScaleType(ImageView.ScaleType.FIT_CENTER);

        return i;
    }

}

Can anyone see what might be causing my memory issues?

There actually several issues:

  • Gallery view cache is broken so each time getView method is called convertView is null see this bug
  • In your adapter you allocate new bitmap on each getView method call so then scrolling happens same bitmaps get decoded again and again So from my point of view you should try to implement you'r own Bitmap cache inside your adapter so memory don't get overthrown with same bitmap objects

you may try using scaledBitmap to reduce the memory usage

if(file.exists()){ 
            bMap = BitmapFactory.decodeFile(filePath); 
            i.setImageBitmap(bMap); 
        } 

to

if(file.exists()){ 
            bMap = BitmapFactory.decodeFile(filePath); 
            bMap = Bitmap.createScaledBitmap(bMap, 100, 100, true);
            i.setImageBitmap(bMap); 
        } 

尝试将图像存储在SD卡或设备内存中,以避免此问题。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM