2011-02-07 3 views
8

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

Он отлично подходит для небольшого количества фотографий. Но если приложение попросит загрузить 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); 
+0

FYI, вышеуказанный код по-прежнему блокирует поток пользовательского интерфейса при загрузке с SD-карты (не рекомендуется поведение). Также похоже, что если вы не дождитесь загрузки всех изображений перед отображением ListView, ListView не будет обновляться даже после того, как изображения будут готовы до тех пор, пока пользователь не прокрутит экран и строки не будут восстановлены. См. Мой связанный [вопрос] (http://stackoverflow.com/q/11712130/403455). –

ответ

9

Матео там идет ваш ответ http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html

А ты молодец!

+0

Спасибо! Я смотрю на последние дни. Я пытаюсь выяснить, как использовать слабые ссылки на изображения, загруженные в html-код textview; а также если моя утечка есть или если входной поток имеет какое-то переполнение, которое захватывает RAM. Как только я это выясню, я отвечу как ответ :) – Mateo

+0

Спасибо, мне это помогло, так как он использует меньше бара, чтобы использовать flushedinputstream для декодирования растрового изображения во время его загрузки; вместо загрузки байтов и декодирования их в растровое изображение. Хотя я, наконец, заметил, что моя главная проблема заключалась в том, что все задачи начали загружаться одновременно. Мне удалось ограничить число задач до 2 загрузок и поставить все остальные очереди. Я обновляю поток, чтобы внести изменения в AsyncTask, чтобы работать так. – Mateo