I am using Fedor's code(https://github.com/thest1/LazyList) for loading bitmaps. I have modified a few lines of code, according to my requirement. I am getting out of memory error when ever the heap memory crosses the threshold. There are various questions posted on the same topic. Most of them suggesting, to use SoftReference and bitmap recycle(). I am using SoftReference but still I am facing problems. And also I am confused about where to use bitmap recycle method.
MemoryCache.java
public class MemoryCache {
private static final String TAG = "MemoryCache";
private static Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(
new LinkedHashMap<String, SoftReference<Bitmap>>(16,0.75f,false));//Last argument true for LRU ordering
private long size=0;//current allocated size
private long limit=30000000;//max memory in bytes
public MemoryCache(){
long cacheSize = Runtime.getRuntime().maxMemory();
setLimit(cacheSize);
}
public void setLimit(long new_limit){
limit=new_limit;
}
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).get();
}catch(NullPointerException ex){
ex.printStackTrace();
return null;
}
}
public void put(String id, Bitmap bitmap){
try{
if(cache.containsKey(id))
size-=getSizeInBytes(cache.get(id).get());
cache.put(id, new SoftReference<Bitmap>(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+5000000){
cache.clear();
}
Log.i(TAG, "Clean cache. New size "+cache.size());
}
public void clear() {
try{
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
cache.clear();
size=0;
}catch(NullPointerException ex){
ex.printStackTrace();
}
}
long getSizeInBytes(Bitmap bitmap) {
if(bitmap==null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
ImageLoader.java
public class ImageLoader {
MemoryCache memoryCache = new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews = Collections
.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
Handler handler = new Handler();
Context con;
ProgressBar pb;
public ImageLoader(Context context) {
fileCache = new FileCache(context);
this.con = context;
executorService = Executors.newFixedThreadPool(5);
}
final int stub_id = R.drawable.icon_loading;
public void DisplayImage(String url, ImageView imageView, ProgressBar pb) {
this.pb = pb;
imageViews.put(imageView, url);
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null) {
pb.setVisibility(View.GONE);
imageView.setImageBitmap(bitmap);
} else {
queuePhoto(url, imageView);
}
}
private void queuePhoto(String url, ImageView imageView) {
PhotoToLoad p = new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url) {
Bitmap result = null;
File f = fileCache.getFile(url);
// from SD cache
Bitmap b = decodeFile(f);
if (b != null)
return b;
// from web
try {
Bitmap bitmap = null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) imageUrl
.openConnection();
conn.setInstanceFollowRedirects(true);
InputStream is = conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
is.close();
conn.disconnect();
bitmap = decodeFile(f);
//Log.v("bitmap size", bitmap.getByteCount() + "");
//bitmap.recycle();
return bitmap;
} catch (Throwable ex) {
ex.printStackTrace();
if (ex instanceof OutOfMemoryError)
memoryCache.clear();
return null;
}
}
// decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
try {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream stream1 = new FileInputStream(f);
BitmapFactory.decodeStream(stream1, null, o);
stream1.close();
final int REQUIRED_SIZE = 70;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
BitmapFactory.Options o2 = new BitmapFactory.Options();
if (f.length() > 300000) {
o2.inSampleSize = 4;
} else if (f.length() > 200000) {
o2.inSampleSize = 2;
} else {
o2.inSampleSize = 1;
}
FileInputStream stream2 = new FileInputStream(f);
Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
stream2.close();
return bitmap;
} catch (FileNotFoundException e) {
} catch (IOException e) {
memoryCache.clear();
e.printStackTrace();
}
return null;
}
// Task for the queue
private class PhotoToLoad {
public String logo_url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i) {
logo_url = u;
imageView = i;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad) {
this.photoToLoad = photoToLoad;
}
@Override
public void run() {
if (imageViewReused(photoToLoad))
return;
Bitmap bmp = getBitmap(photoToLoad.logo_url);
// Log.v("bitmap size",bmp.getByteCount()+"");
memoryCache.put(photoToLoad.logo_url, bmp);
if (imageViewReused(photoToLoad))
return;
BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
//bmp.recycle();
// Activity a=(Activity)photoToLoad.imageView.getContext();
// a.runOnUiThread(bd);
handler.post(bd);
}
}
boolean imageViewReused(PhotoToLoad photoToLoad) {
String tag = imageViews.get(photoToLoad.imageView);
if (tag == null || !tag.equals(photoToLoad.logo_url))
return true;
return false;
}
// Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable {
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
bitmap = b;
photoToLoad = p;
}
public void run() {
if (imageViewReused(photoToLoad))
return;
if (bitmap != null) {
//bitmap.recycle();
// pb.setVisibility(View.GONE);
photoToLoad.imageView.setImageBitmap(bitmap);
//bitmap.recycle();
} else {
// photoToLoad.imageView.setImageResource(stub_id);
// pb.setVisibility(View.VISIBLE);
}
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
}
Attached the Logcat output:
01-21 16:54:47.348: D/skia(20335): --- decoder->decode returned false
01-21 16:54:47.408: I/dalvikvm-heap(20335): Clamp target GC heap from 69.438MB to 64.000MB
01-21 16:54:47.408: D/dalvikvm(20335): GC_FOR_ALLOC freed 67K, 5% free 62767K/65416K, paused 54ms, total 54ms
01-21 16:54:47.408: I/dalvikvm-heap(20335): Forcing collection of SoftReferences for 228816-byte allocation
01-21 16:54:47.468: I/dalvikvm-heap(20335): Clamp target GC heap from 69.438MB to 64.000MB
01-21 16:54:47.468: D/dalvikvm(20335): GC_BEFORE_OOM freed <1K, 5% free 62767K/65416K, paused 64ms, total 64ms
01-21 16:54:47.468: E/dalvikvm-heap(20335): Out of memory on a 228816-byte allocation.
01-21 16:54:47.468: I/dalvikvm(20335): "pool-21-thread-4" prio=5 tid=63 RUNNABLE
01-21 16:54:47.468: I/dalvikvm(20335): | group="main" sCount=0 dsCount=0 obj=0x42e4e878 self=0x67693b00
01-21 16:54:47.468: I/dalvikvm(20335): | sysTid=20520 nice=0 sched=0/0 cgrp=apps handle=1735190240
01-21 16:54:47.468: I/dalvikvm(20335): | state=R schedstat=( 2851815000 268321000 1461 ) utm=276 stm=9 core=0
01-21 16:54:47.468: I/dalvikvm(20335): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-21 16:54:47.468: I/dalvikvm(20335): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:529)
01-21 16:54:47.468: I/dalvikvm(20335): at com.main.util.ImageLoader.decodeFile(ImageLoader.java:211)
01-21 16:54:47.468: I/dalvikvm(20335): at com.main.util.ImageLoader.getBitmap(ImageLoader.java:85)
01-21 16:54:47.468: I/dalvikvm(20335): at com.main.util.ImageLoader.access$1(ImageLoader.java:79)
01-21 16:54:47.468: I/dalvikvm(20335): at com.main.util.ImageLoader$PhotosLoader.run(ImageLoader.java:244)
01-21 16:54:47.468: I/dalvikvm(20335): at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390)
01-21 16:54:47.468: I/dalvikvm(20335): at java.util.concurrent.FutureTask.run(FutureTask.java:234)
01-21 16:54:47.468: I/dalvikvm(20335): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
01-21 16:54:47.468: I/dalvikvm(20335): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
01-21 16:54:47.468: I/dalvikvm(20335): at java.lang.Thread.run(Thread.java:856)