title: Android java.lang.OutOfMemoryError?
body: <pre><code>04-25 08:19:10.111    2431-2603/com.example.francesco.guidedautorewithtabs E/art﹕ Throwing OutOfMemoryError "Failed to allocate a 4194316 byte allocation with 1983651 free bytes and 1937KB until OOM"
  04-25 08:19:10.114    2431-2603/com.example.francesco.guidedautorewithtabs E/AndroidRuntime﹕ FATAL EXCEPTION: GLThread 285
  Process: com.example.francesco.guidedautorewithtabs, PID: 2431
  java.lang.OutOfMemoryError: Failed to allocate a 4194316 byte allocation with 1983651 free bytes and 1937KB until OOM
          at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
          at android.graphics.Bitmap.nativeCreate(Native Method)
          at android.graphics.Bitmap.createBitmap(Bitmap.java:817)
          at android.graphics.Bitmap.createBitmap(Bitmap.java:794)
          at android.graphics.Bitmap.createBitmap(Bitmap.java:761)
          at com.google.maps.api.android.lib6.gmm6.o.c.l.a(Unknown Source)
          at com.google.maps.api.android.lib6.gmm6.o.c.o.a(Unknown Source)
          at com.google.maps.api.android.lib6.gmm6.o.c.o.a(Unknown Source)
          at com.google.maps.api.android.lib6.gmm6.o.c.o.b(Unknown Source)
          at com.google.maps.api.android.lib6.gmm6.o.c.b.ak.a(Unknown Source)
          at com.google.maps.api.android.lib6.gmm6.o.c.b.as.a(Unknown Source)
          at com.google.maps.api.android.lib6.gmm6.o.x.a(Unknown Source)
          at com.google.maps.api.android.lib6.gmm6.o.l.a(Unknown Source)
          at com.google.maps.api.android.lib6.gmm6.o.l.b(Unknown Source)
          at com.google.maps.api.android.lib6.gmm6.o.cj.g(Unknown Source)
          at com.google.maps.api.android.lib6.gmm6.o.cj.run(Unknown Source)
  </code></pre>
  
  <p>In my Android code i have a lot of images that have to be displayed into different fragment. Several of this are stored in the assets folder, other picked up from an http request. For this reason i implement image caching in order to do not go against the java.lang.OutOfMemoryError, but nevertheless this error seems to persist. How can i resolve this problem? Below my implementation of image caching. </p>
  
  <p>ImageLoader</p>
  
  <pre><code>public class ImageLoader {
  MemoryCache memoryCache=new MemoryCache();
  FileCache fileCache;
  private Map&lt;ImageView, String&gt; imageViews=Collections.synchronizedMap(new WeakHashMap&lt;ImageView, String&gt;());
  ExecutorService executorService;
  private Context context;
  
  public ImageLoader(Context context){
      this.context=context;
      fileCache=new FileCache(context);
      executorService=Executors.newFixedThreadPool(5);
  }
  
  final int stub_id= R.drawable.no_image;
  public void DisplayImage(String url, ImageView imageView)
  {
      imageViews.put(imageView, url);
      Bitmap bitmap=memoryCache.get(url);
      if(bitmap!=null)
          imageView.setImageBitmap(bitmap);
      else
      {
          queuePhoto(url, imageView);
          imageView.setImageResource(stub_id);
      }
  }
  
  private void queuePhoto(String url, ImageView imageView)
  {
      PhotoToLoad p=new PhotoToLoad(url, imageView);
      executorService.submit(new PhotosLoader(p));
  }
  
  private Bitmap getBitmapFromAsset(String strName)
  {
      AssetManager assetManager = context.getAssets();
      InputStream istr = null;
      try {
          istr = assetManager.open(strName);
      } catch (IOException e) {
          e.printStackTrace();
      }
      Bitmap bitmap = BitmapFactory.decodeStream(istr);
      return bitmap;
  }
  
  private Bitmap getBitmap(String url)
  {
      File f=fileCache.getFile(url);
  
      //from SD cache
      Bitmap b = decodeFile(f);
      if(b!=null)
          return b;
  
      //from assets
      Bitmap bm = getBitmapFromAsset(url);
  
      if(bm!=null)
          return bm;
  
      //from web
      try {
          Bitmap bitmap=null;
          URL imageUrl = new URL(url);
          HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
          conn.setConnectTimeout(30000);
          conn.setReadTimeout(30000);
          conn.setInstanceFollowRedirects(true);
          InputStream is=conn.getInputStream();
          OutputStream os = new FileOutputStream(f);
          Utils.CopyStream(is, os);
          os.close();
          bitmap = decodeFile(f);
          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 {
          //decode image size
          BitmapFactory.Options o = new BitmapFactory.Options();
          o.inJustDecodeBounds = true;
          BitmapFactory.decodeStream(new FileInputStream(f),null,o);
  
          //Find the correct scale value. It should be the power of 2.
          final int REQUIRED_SIZE=70;
          int width_tmp=o.outWidth, height_tmp=o.outHeight;
          int scale=1;
          while(true){
              if(width_tmp/2&lt;REQUIRED_SIZE || height_tmp/2&lt;REQUIRED_SIZE)
                  break;
              width_tmp/=2;
              height_tmp/=2;
              scale*=2;
          }
  
          //decode with inSampleSize
          BitmapFactory.Options o2 = new BitmapFactory.Options();
          o2.inSampleSize=scale;
          return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
      } catch (FileNotFoundException e) {}
      return null;
  }
  
  //Task for the queue
  private class PhotoToLoad
  {
      public String url;
      public ImageView imageView;
      public PhotoToLoad(String u, ImageView i){
          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.url);
          memoryCache.put(photoToLoad.url, bmp);
          if(imageViewReused(photoToLoad))
              return;
          BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
          Activity a=(Activity)photoToLoad.imageView.getContext();
          a.runOnUiThread(bd);
      }
  }
  
  boolean imageViewReused(PhotoToLoad photoToLoad){
      String tag=imageViews.get(photoToLoad.imageView);
      if(tag==null || !tag.equals(photoToLoad.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)
              photoToLoad.imageView.setImageBitmap(bitmap);
          else
              photoToLoad.imageView.setImageResource(stub_id);
      }
  }
  
  public void clearCache() {
      memoryCache.clear();
      fileCache.clear();
  }
  }
  </code></pre>
  
  <p>FileCache</p>
  
  <pre><code>public class FileCache {
  
  private File cacheDir;
  
  public FileCache(Context context){
      //Find the dir to save cached images
      if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
          cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"TTImages_cache");
      else
          cacheDir=context.getCacheDir();
      if(!cacheDir.exists())
          cacheDir.mkdirs();
  }
  
  public File getFile(String url){
      //I identify images by hashcode. Not a perfect solution, good for the demo.
      String filename=String.valueOf(url.hashCode());
      //Another possible solution (thanks to grantland)
      //String filename = URLEncoder.encode(url);
      File f = new File(cacheDir, filename);
      return f;
  
  }
  
  public void clear(){
      File[] files=cacheDir.listFiles();
      if(files==null)
          return;
      for(File f:files)
          f.delete();
  }
  
  }
  </code></pre>
  
  <p>MemoryCache</p>
  
  <pre><code>public class MemoryCache {
  
  private static final String TAG = "MemoryCache";
  private Map&lt;String, Bitmap&gt; cache=Collections.synchronizedMap(
          new LinkedHashMap&lt;String, Bitmap&gt;(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 25% of available heap size
      setLimit(Runtime.getRuntime().maxMemory()/4);
  }
  
  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){
          ex.printStackTrace();
          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&gt;limit){
          Iterator&lt;Entry&lt;String, Bitmap&gt;&gt; iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated
          while(iter.hasNext()){
              Entry&lt;String, Bitmap&gt; entry=iter.next();
              size-=getSizeInBytes(entry.getValue());
              iter.remove();
              if(size&lt;=limit)
                  break;
          }
          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();
  }
  }
  </code></pre>
  
  <p>Utils</p>
  
  <pre><code>public class Utils {
  public static void CopyStream(InputStream is, OutputStream os)
  {
      final int buffer_size=1024;
      try
      {
          byte[] bytes=new byte[buffer_size];
          for(;;)
          {
              int count=is.read(bytes, 0, buffer_size);
              if(count==-1)
                  break;
              os.write(bytes, 0, count);
          }
      }
      catch(Exception ex){}
  }
  }
  </code></pre>