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

Имам приложение, което изтегля изображения от сървър, съхранява ги на sd картата и след това ги показва на потребителя като слайдшоу. Приложението има няколко различни слайдшоута, които потребителят може да гледа. Проблемът ми е, че след като прегледам няколко галерии, получавам грешка за липса на памет. Направих доста гугъл и прочетох статията на Romain Guy за избягване на изтичане на памет около 20 пъти. Опитах се да копирам неговия метод unbindDrawables(), но методът but 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 вместо Gallery във връзка с 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