Управление несколькими асинхронными задачами для загрузки нескольких изображений из HTML-кода, утечка оперативной памяти, есть идеи? - PullRequest
8 голосов
/ 07 февраля 2011

Я занимаюсь разработкой приложения для Android.Теперь я разбираю bbcode в html и отображаю его внутри текстового представления, текстовое представление находится внутри пользовательского списка.Я использую Html.ImageGetter () для отображения изображений, загруженных из AsyncTask.

Отлично работает для небольшого количества картинок.Но если приложение просят загрузить 40-50 картинок, создается 40-50 задач, и это становится беспорядком.Каждая задача открывает поток для загрузки изображений.После этого он декодирует байты в растровые изображения, изменяет их размер, сохраняет их на SD-карту и перерабатывает растровые изображения.

Теперь, если приложение загружает все эти изображения одновременно, оно использует огромное количество оперативной памяти.Мне удалось заставить его пройти 48 мб.Между 16 и 48 существует большой разрыв :(. Я искал, как решить эту проблему. Я скачал код AsyncTask из Google:

http://google.com/codesearch/p?hl=en&sa=N&cd=2&ct=rc#uX1GffpyOZk/core/java/android/os/AsyncTask.java&q=lang:java%20AsyncTask

и установил размер пула 3.Но это не помогло. Я действительно не могу понять, где я теряю оперативную память. Как только я ставлю большую очередь задач, мой оперативный компьютер сходит с ума. После получения нескольких изображений становится хуже. Я не думаю, чтоэто изображения, так как я могу получить до 30 МБ до того, как отобразится любое изображение. Само приложение, включая представление, информацию и его сервис, использует 13 МБ, все остальное здесь просочилось.

Делает ли сама очередь очередьвыделение больших оперативной памяти? Или Html.ImageGetter () как-то пропускает огромный объем памяти? Есть ли лучший способ сделать это?

Здесь я загружаю изображения:

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<URL, Integer, Bitmap> {

    @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 < length) {
                if (length < buffersize) {
                    read = is.read(imageData, downloaded, length);
                } else if ((length - downloaded) <= 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();
}

И здесь я вызываю Html.ImageGetter (), это внутри списка getView:

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

Спасибо!

Наконец проблема заключалась в том, что все задачи загружались одновременно. Таким образом, 40 изображенийгде распределяется в оперативной памяти при загрузке. Мне удалосьограничить количество запущенных задач, выполнив следующие изменения в AsyncTask:

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<Runnable> sWorkQueue =
        new LinkedBlockingQueue<Runnable>(100);

1 Ответ

9 голосов
/ 07 февраля 2011
...