0

According to my application first I copy all the images from my resources to the internal memory and then on Image Slide to left or right I get the image from memory with its index and show it there. And I'm doing it with AsynTask. And after around 10 images are shown application goes to black screen and log cat says "external allocation too large for this process." According to the things I read here I think the problem is about AsyncTask, I cannot free the memory which has been used for these tasks. I have three different Activities which are used to show images as a gallery, and each of these activities are using asyncTask to show the images.

Here is some of my code below, and any help will be appreciatead, Thanks in advance. Here is my Activity used to execute image downloader according to sliding images.

lid1 = new LocalImageDownloader(imageSwitcher, myContext, path, nameList.get(curIndex) );
            lid1.execute();

            imageSwitcher.setOnTouchListener(new OnTouchListener() {
                public boolean onTouch(View v, MotionEvent event) {

                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        downX = (int) event.getX(); 
                        Log.i("event.getX()", " downX " + downX);
                        return true;
                    } 

                    else if (event.getAction() == MotionEvent.ACTION_UP) {
                        upX = (int) event.getX(); 
                        Log.i("event.getX()", " upX " + downX);
                        if (upX - downX > 100) {

                            //curIndex  current image index in array viewed by user
                            curIndex--;
                            if (curIndex < 0) {
                                curIndex = imageList.size()-1;
                            }

                            imageSwitcher.setInAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_in_left));
                            imageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_out_right));

                            lid1.cancel(true);
                            lid1 = new LocalImageDownloader(imageSwitcher, myContext, path, nameList.get(curIndex) );
                            lid1.execute();
                        }

                        else if (downX - upX > -100) {

                            curIndex++;
                            if (curIndex == imageList.size() ) {
                                curIndex = 0;
                            }

                            imageSwitcher.setInAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_in_right));
                            imageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_out_left));

                            lid1.cancel(true);
                            lid1 = new LocalImageDownloader(imageSwitcher, myContext, path, nameList.get(curIndex) );
                            lid1.execute();
                        }
                        return true;
                    }
                    return false;
                }
            });

and this is my AsyncTask to get images from internal memory,

public class LocalImageDownloader extends AsyncTask<String, Void, Bitmap> {

String url;
Drawable d;
Context myContext;

String path;
String fileName;

ProgressDialog dialog;
int REQUIRED_SIZE=600;

private final WeakReference<ImageSwitcher> imageViewReference;

public LocalImageDownloader(ImageSwitcher imageSwitcher,Context myContext, String path, String fileName) {
    this.myContext = myContext;
    this.path = path;
    this.fileName = fileName;
    imageViewReference = new WeakReference<ImageSwitcher>(imageSwitcher);
}

@Override
protected Bitmap doInBackground(String... urls) {
    publishProgress();
    return null;
}

@Override
protected void onPreExecute() {
    dialog = ProgressDialog.show(myContext, "", "Loading Images...", true);
    super.onPreExecute();
}

@Override
protected void onPostExecute(Bitmap result) {

    try {
        if (imageViewReference != null) {
            ImageSwitcher imageSwitcher = imageViewReference.get();
            if (imageSwitcher != null) {
                imageSwitcher.setImageDrawable(getLocalImage());
            }
        }
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    dialog.dismiss();
}

public Drawable getLocalImage() throws IOException {

    File file = new File(path,fileName);

    //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(new FileInputStream(file),null,o);

    //The new size we want to scale to

    //Find the correct scale value. It should be the power of 2.
    int scale=1;
    while(o.outWidth/scale/2>=this.REQUIRED_SIZE && o.outHeight/scale/2>=this.REQUIRED_SIZE)
        scale*=2;

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize=scale;
    o.inJustDecodeBounds = false;

    return new BitmapDrawable(BitmapFactory.decodeStream(new FileInputStream(file), null, o2));
}

}

EDIT: I have applied some of the ways to use bitmaps more efficiently and now I'm pushing them to the memory but I still have almost the same error. After some of the images are stored in memory, for some of the images I get black screen and having the same error."external allocation too large for this process." Any idea how to do it ?

Here is the memory cache code below, and I'm sending my MemoryCache object to AsyncTask as a parameter.

public class MemoryCache {

private static final String TAG = "MemoryCache";
private Map<String, Bitmap> cache=Collections.synchronizedMap(
        new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering
private long size=0;//current allocated size
private long limit=1000000;//max memory in bytes

public MemoryCache(){
    //use 50% of available heap size
    setLimit(Runtime.getRuntime().maxMemory()/2);
}

public void setLimit(long new_limit){
    limit=new_limit;
    Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
}

public Bitmap get(String id){
    try{
        if(!cache.containsKey(id))
            return null;
        //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 
        return cache.get(id);
    }catch(NullPointerException ex){
        return null;
    }
}

public void put(String id, Bitmap bitmap){
    try{
        if(cache.containsKey(id))
            size-=getSizeInBytes(cache.get(id));
        cache.put(id, bitmap);
        size+=getSizeInBytes(bitmap);
        checkSize();
    }catch(Throwable th){
        th.printStackTrace();
    }
}

private void checkSize() {
    Log.i(TAG, "cache size="+size+" length="+cache.size());
    if(size>limit){
        Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated  
        while(iter.hasNext()){
            Entry<String, Bitmap> entry=iter.next();
            size-=getSizeInBytes(entry.getValue());
            iter.remove();
            if(size<=limit)
                break;
        }
        Log.i(TAG, "Clean cache. New size "+cache.size());
    }
}

public void clear() {
    cache.clear();
}

long getSizeInBytes(Bitmap bitmap) {
    if(bitmap==null)
        return 0;
    return bitmap.getRowBytes() * bitmap.getHeight();
}

public boolean contains(String key) {

    if(cache.containsKey(key)) {
        return true;
    }

    return false;
}

}

osayilgan
  • 5,873
  • 7
  • 47
  • 68

3 Answers3

0

It has nothing to do with AsyncTask. You will have to use Bitmaps more efficiently. Search for Bitmap OOM; do some sampling of your downloaded images.as in here

Also search for Android SoftReference.

Community
  • 1
  • 1
Shashank Degloorkar
  • 3,151
  • 4
  • 32
  • 50
  • I tried something for using bitmaps efficiently but I think its not about using it, because my images are around 50KB and size is also so small. – osayilgan May 07 '12 at 10:32
0

Try this link for managing your bitmap cache, there are many solutions for preventing the outofMemory exception. http://developer.android.com/training/displaying-bitmaps/index.html

mayank_droid
  • 1,015
  • 10
  • 19
  • isn't it for big sized images ? Because my images are around 50-100KB so I think it doesnt matter that much. ??? – osayilgan May 07 '12 at 11:52
  • its not about size of your bitmaps, but also the how many are they too.!! if you getting the error, bitmap out of memory, then exactly you need to manage them by giving soft refrence or storing them on sdcard. cos memory allocated to any application is 16 MB in normal devices, so even you have to take care when you developing application based on lots of images. so better you use any of the ways described in that link.:) – mayank_droid May 07 '12 at 12:43
  • I will try it as soon as possible, and let you know if it works, Thanks for the answer bytheway. – osayilgan May 07 '12 at 12:57
  • 1
    hey man, thx for the answer it worked a bit, at least it doesn't crush anymore but I still have the same error. If the memory doesn't get enough for the bitmap, it still doesn't show it, I just get black screen for some of the images. Any idea how to do it ? I will update my question with the new code. – osayilgan May 08 '12 at 10:56
  • can not understand your problem at all, is it showing image after sometime? or does not show at all? if its showing after sometime then only thing you need to do is to show loading till image gets loaded. And important thing also that if you currently working on emulator then try it on device, first. – mayank_droid May 08 '12 at 14:05
  • I'm trying on device, the problem is I'm having black screen becuse decoder returns null since there is an error which says "external allocation too large for this process." I'm also not sure about the image sizes cuz my images are around 100KB and whenever I put them to the cache It goes something like 2.5MB in the LogCat and even I reserve the half of the avaible memory It doesnt get enough then returns null for decoder. – osayilgan May 08 '12 at 17:01
  • well, I really dont know how many images you want to load, better you try some links because as you saying the cache is about 2.5 MB then if it is 100 KB image then, better you use lazy loading kind of thing. i refer you a this website http://www.androidhive.info, there are couple of examples for managing memory as you want. pls try this. hope you find something useful from this website – mayank_droid May 09 '12 at 05:42
  • 1
    I have found a way and now it works quite well, instead of getting images with file streams now I do it by setting the drawable with the code below. Thanks for the help. `ImageView imageView = (ImageView) findViewById(R.id.image); File filePath = getFileStreamPath(fileName); imageView.setImageDrawable(Drawable.createFromPath(fileName));` – osayilgan May 09 '12 at 15:14
0

I have found a way and now it works quite well, instead of getting images with file streams and bitmaps now I do it by setting the drawable with the code below.As for the images downloaded from internet , anybody who wants to do something like that, just simply download images and save it either in internal memory or in external memory then just give the path to the image.

ImageView imageView = (ImageView) findViewById(R.id.image); 
File filePath = getFileStreamPath(fileName);
imageView.setImageDrawable(Drawable.createFromPath(filePath));
osayilgan
  • 5,873
  • 7
  • 47
  • 68
  • 1
    I didn't take a look about it but I think it is just getting images from input stream for each time we slide the image instead of loading the images to memory.Because it is not that much fast as it is in memory. – osayilgan May 10 '12 at 07:49