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