title: Managing multiple asynctask to download multiple images from html code, leaking ram, any ideas?
body: <p>I'm developing an android app. Now I'm parsing bbcode to html and display it inside a textview, the textview is inside a custom listview. I use Html.ImageGetter() to display the images downloaded from AsyncTask. </p>
  
  <p>It works great for a low number of pictures. But if the app is asked to download 40-50 pictures, 40-50 tasks are created and it becomes a mess. Each task opens a stream to download the images. After that it decodes the bytes into bitmaps, resize them, save them to the sdcard and recycles the bitmaps. </p>
  
  <p>Now if the app is loading all this images at the same time it uses a huge amount of ram. I managed to make it pass 48 mb. There is a big gap between 16 and 48 :(. I searched on how to solve this. I downloaded AsyncTask code from google: </p>
  
  <p><a href="http://google.com/codesearch/p?hl=en&amp;sa=N&amp;cd=2&amp;ct=rc#uX1GffpyOZk/core/java/android/os/AsyncTask.java&amp;q=lang:java%20AsyncTask" rel="nofollow noreferrer">http://google.com/codesearch/p?hl=en&amp;sa=N&amp;cd=2&amp;ct=rc#uX1GffpyOZk/core/java/android/os/AsyncTask.java&amp;q=lang:java%20AsyncTask</a></p>
  
  <p>And set the pool size to 3. But this didn't helped. I really can't figure out where I'm loosing ram. As soon as I put a big task queue my ram goes crazy. After a few images are received it gets worst. I don't think it is the images since I can get to 30 mb before any image is displayed. The app itself including the view, information and its service uses 13 mb, all the rest is leaked here.</p>
  
  <p>Does the queue itself make big ram allocations? Or is the Html.ImageGetter() leaking a huge amount of memory somehow? Is there a better way to do this? </p>
  
  <p>Here I load the images:</p>
  
  <pre><code>public void LoadImages(String source) {
  
      myurl = null;
      try {
          myurl = new URL(source);
      } catch (MalformedURLException e) {
          e.printStackTrace();
      }
  
      new DownloadImageFromPost().execute(myurl);
  }
  
  private class DownloadImageFromPost extends AsyncTask&lt;URL, Integer, Bitmap&gt; {
  
      @Override
      protected Bitmap doInBackground(URL... params) {
          URL url;
          Log.d(TAG, "Starting new image download");
          try {
              url = params[0];
              HttpURLConnection connection = (HttpURLConnection) url
              .openConnection();
              int length = connection.getContentLength();
              InputStream is = (InputStream) url.getContent();
              byte[] imageData = new byte[length];
              int buffersize = (int) Math.ceil(length / (double) 100);
              int downloaded = 0;
              int read;
              while (downloaded &lt; length) {
                  if (length &lt; buffersize) {
                      read = is.read(imageData, downloaded, length);
                  } else if ((length - downloaded) &lt;= buffersize) {
                      read = is.read(imageData, downloaded, length
                              - downloaded);
                  } else {
                      read = is.read(imageData, downloaded, buffersize);
                  }
                  downloaded += read;
                  publishProgress((downloaded * 100) / length);
              }
              Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0,
                      length);
              if (bitmap != null) {
                  Log.i(TAG, "Bitmap created");
              } else {
                  Log.i(TAG, "Bitmap not created");
              }
              is.close();
              return bitmap;
  
          } catch (MalformedURLException e) {
              Log.e(TAG, "Malformed exception: " + e.toString());
  
          } catch (IOException e) {
              Log.e(TAG, "IOException: " + e.toString());
  
          } catch (Exception e) {
  
          }
  
  
      return null;
  
  }
  
  protected void onPostExecute(Bitmap result) {
  
          String name = Environment.getExternalStorageDirectory() + "/tempthumbs/" + myurl.toString().hashCode() +".jpg";
          String rname = Environment.getExternalStorageDirectory() + "/tempthumbs/" + myurl.toString().hashCode() +"-t.jpg";
          try {
              if (result != null) {
                      hasExternalStoragePublicPicture(name); 
                      ImageManager manager = new ImageManager(context);
                      Bitmap rezised = manager.resizeBitmap(result, 300, 300);
                      saveToSDCard(result, name,  myurl.toString().hashCode() +".jpg");
                      saveToSDCard(rezised, rname, myurl.toString().hashCode() +"-t.jpg");
                      result.recycle();
                      rezised.recycle();
  
              } else {
  
              }
          } catch(NullPointerException e) {
          }
  
      Log.d(TAG, "Sending images loaded announcement");
      Intent i = new Intent(IMAGE_LOADED);
      i.putExtra("image",  name);
      i.putExtra("source", myurl.toString());
      i.putExtra("class", true);
      context.sendBroadcast(i);
  
  }
  
  }
  
  
      private boolean hasExternalStoragePublicPicture(String name) {
      File file = new File(name);
      if (file != null) {
          file.delete();
      }
  
          try {
              file.createNewFile();
  
          } catch (IOException e) {
          e.printStackTrace();
  
  
          }
  
      return file.exists();
  }
  
  public void saveToSDCard(Bitmap bitmap, String name, String nam) {
      boolean mExternalStorageAvailable = false;
      boolean mExternalStorageWriteable = false;
      String state = Environment.getExternalStorageState();
      if (Environment.MEDIA_MOUNTED.equals(state)) {
          mExternalStorageAvailable = mExternalStorageWriteable = true;
          Log.v(TAG, "SD Card is available for read and write "
                  + mExternalStorageAvailable + mExternalStorageWriteable);
          saveFile(bitmap, name, nam);
      } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
          mExternalStorageAvailable = true;
          mExternalStorageWriteable = false;
          Log.v(TAG, "SD Card is available for read "
                  + mExternalStorageAvailable);
      } else {
          mExternalStorageAvailable = mExternalStorageWriteable = false;
          Log.v(TAG, "Please insert a SD Card to save your Video "
                  + mExternalStorageAvailable + mExternalStorageWriteable);
      }
  }
  
  private void saveFile(Bitmap bitmap, String fullname, String nam) {
  
      ContentValues values = new ContentValues();
  
      File outputFile = new File(fullname);
      values.put(MediaStore.MediaColumns.DATA, outputFile.toString());
      values.put(MediaStore.MediaColumns.TITLE, nam);
      values.put(MediaStore.MediaColumns.DATE_ADDED, System
              .currentTimeMillis());
      values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
      Uri uri = context.getContentResolver().insert(
              android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
  
              values);;
  
      try {
          OutputStream outStream = context.getContentResolver()
                  .openOutputStream(uri);
          bitmap.compress(Bitmap.CompressFormat.JPEG, 95, outStream);
  
          outStream.flush();
          outStream.close();
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      }
      bitmap.recycle();
  }
  </code></pre>
  
  <p>And here i call Html.ImageGetter(), this is inside a list getView:</p>
  
  <pre><code>holder.content.setText(Html.fromHtml(
     processor.preparePostText(posts.get(position).post_content),
     new Html.ImageGetter() {
  
        @Override public Drawable getDrawable(String source) {
  
           Log.d("Forum Service", "image source: " + source);
           if (imageSources.contains(source)) {
              for (int x = 0; x &lt; imageSources.size(); x++) {
                 if (source.equals(imageSources.get(x))) {
  
                    String tmp = oImages.get(x);
                    tmp = tmp.substring(0, tmp.length() - 4);
                    tmp = tmp + "-t.jpg";
                    Drawable d = Drawable.createFromPath(tmp);
                    try {
                       d.setBounds(0, 0, d.getIntrinsicWidth(),
                          d.getIntrinsicHeight());
                    } catch (NullPointerException e) {
  
                    }
                    Log.d("Forum Service", "Loaded image froms sdcard");
                    return d;
                 }
              }
  
           } else if (notLoadedImages.contains(source)) {
              Log.d("Forum Service", "Waiting for image");
              return null;
           } else {
              notLoadedImages.add(source);
              LoadAllIcons loader = new LoadAllIcons(context);
              loader.LoadImages(source);
              Log.d("Forum Service", "Asked for image");
              return null;
           }
           return null;
        }
  
     }, null));
  </code></pre>
  
  <p>Thanks!</p>
  
  <p>Finally the problem was that all Tasks loaded at the same time. Therefor 40 images where allocated in ram while downloading. I managed to limit the amount of running tasks by doing this modifications on AsyncTask:</p>
  
  <pre><code>private static final int CORE_POOL_SIZE = 2;
  private static final int MAXIMUM_POOL_SIZE = 2;
  private static final int KEEP_ALIVE = 1;
  
  private static final BlockingQueue&lt;Runnable&gt; sWorkQueue =
          new LinkedBlockingQueue&lt;Runnable&gt;(100);
  </code></pre>