Android: outOfMemoryError с растровыми изображениями в галерее

У меня есть приложение, которое загружает изображения с сервера, сохраняет их на SD-карте, а затем отображает их пользователю в виде слайд-шоу. Приложение имеет ряд различных слайд-шоу, которые пользователь может просматривать. Моя проблема в том, что после просмотра нескольких галерей я получаю сообщение об ошибке нехватки памяти. Я довольно много погуглил и прочитал статью Ромена Гая «Избегание утечек памяти» около 20 раз. Я попытался воспроизвести его метод unbindDrawables(), но метод removeAllViews() не может быть вызван для объектов галереи. Я также пытался вызвать recycle() для всех растровых изображений, которые я мог, но когда я делаю это с растровым изображением в адаптере, приложение выдает ошибку, как только я открываю галерею изображений. Я также попытался перекодировать слайд-шоу, чтобы создать все растровые изображения вне адаптера, а затем передать их в виде массива — это позволяет мне перебирать массив растровых изображений и вызывать recycle() для каждого из них в методе onDestroy в слайд-шоу. активность — но на самом деле это, кажется, делает утечку хуже, а не лучше.

Вот код для моей активности слайд-шоу:

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);
    }

}

А вот мой код для моего класса SlideshowAdapter:

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;
    }

}

Может ли кто-нибудь увидеть, что может быть причиной моих проблем с памятью?


person Druin    schedule 11.10.2011    source источник


Ответы (3)


вы можете попробовать использовать scaledBitmap, чтобы уменьшить использование памяти

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

в

if(file.exists()){ 
            bMap = BitmapFactory.decodeFile(filePath); 
            bMap = Bitmap.createScaledBitmap(bMap, 100, 100, true);
            i.setImageBitmap(bMap); 
        } 
person MKJParekh    schedule 11.10.2011
comment
FWIW В итоге я переписал свой код, чтобы использовать GridView вместо галереи в сочетании с Bitmap.createScaledBitmap(). - person Druin; 27.08.2012

На самом деле есть несколько проблем:

  • Кэш представления галереи поврежден, поэтому каждый раз, когда вызывается метод getView convertView, имеет значение null, см. это ошибка
  • В вашем адаптере вы выделяете новое растровое изображение при каждом вызове метода getView, поэтому затем происходит прокрутка, одни и те же растровые изображения декодируются снова и снова. Поэтому, с моей точки зрения, вы должны попытаться реализовать свой собственный кеш Bitmap внутри вашего адаптера, чтобы память не была перегружена с одинаковыми растровыми объектами
person Nikolay Ivanov    schedule 11.10.2011
comment
Когда вы говорите, что кеш просмотра Галереи поврежден, вы имеете в виду, что я сломал его, то есть в моем методе getView? или Вы имеете в виду, что что-то в Android сломано? Кроме того, есть ли шанс, что вы можете предоставить ссылку на пример того, как я буду реализовывать свой собственный кеш Bitmap? - person Druin; 19.10.2011

Попробуйте сохранить изображения на SD-карте или в памяти устройства, чтобы избежать этой проблемы.

person Thiru    schedule 11.10.2011