2011-02-07 11 views
8

Android 앱을 개발 중입니다. 이제 html에 bbcode를 파싱하고 textview 내부에 표시합니다. textview는 사용자 정의 listview 안에 있습니다. AsyncTask에서 다운로드 한 이미지를 표시하려면 Html.ImageGetter()를 사용합니다.여러 asynctask를 관리하여 html 코드에서 여러 이미지를 다운로드하고 램을 유출 했습니까?

적은 수의 사진에 유용합니다. 그러나 앱에 40-50 장의 사진을 다운로드하도록 요청하면 40-50 개의 작업이 만들어지고 엉망이됩니다. 각 작업은 스트림을 열어 이미지를 다운로드합니다. 그런 다음 바이트를 비트 맵으로 디코딩하고 크기를 조정 한 다음 sdcard에 저장하고 비트 맵을 재활용합니다.

이제 앱에서이 모든 이미지를 동시에로드하는 경우 엄청난 양의 RAM을 사용합니다. 나는 48 메가 비트를 통과하도록 만들었다. .. 나는이 문제를 해결하는 방법에 대한 검색 :(16과 48 사이에 큰 틈이 나는 구글에서 AsyncTask를 코드 다운로드 :

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으로 풀 크기를 설정하지만이 도움이되지 않았다 . 나는 정말로 내가 숫양을 잃어 버리는 것을 알아낼 수 없다. 큰 작업 대기열을 열 자마자 숫양이 미쳐 간다. 약간의 이미지가 수신 된 후에 그것은 최악이된다. 나는 그것이 이미지이기 때문에 생각하지 않는다. 보기, 정보 및 서비스를 포함하여 응용 프로그램 자체가 13MB를 사용하며 나머지는 모두 여기에서 유출됩니다.

대기열 자체가 큰 RAM 할당을 수행합니까, 아니면 HTML .ImageGetter()가 엄청난 양을 누출했습니다. 어떻게 든 기억의? 이 작업을 수행하는 더 좋은 방법이 있습니까?

가 여기에 내가 이미지를로드 : 내가) 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(); 
} 

을 그리고,이 목록의 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)); 

감사합니다!

마지막으로 문제는 모든 작업이 동시에로드된다는 것입니다. 따라서 다운로드하는 동안 RAM에 40 개의 이미지가 할당됩니다.

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 카드에서로드하는 동안 UI 스레드를 여전히 잠급니다 (권장되지 않음). 또한 ListView를 표시하기 전에 모든 이미지가로드 될 때까지 기다리지 않으면 사용자가 화면에서 스크롤하여 행을 다시 만들 때까지 이미지가 준비된 후에도 ListView가 업데이트되지 않습니다. 내 관련 [질문] (http://stackoverflow.com/q/11712130/403455)를 참조하십시오. –

답변

9

마테오 답 http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html

간다 그리고 당신은 완료 : 나는 AsyncTask를에 수정을 수행하여 실행 작업의 양을 제한하기 위해 관리!

+0

감사합니다 !! 나는 마지막 날을 바라보고있다. 내가 textview의 HTML 코드에로드 된 이미지를 약한 참조를 사용하는 방법을 알아 내려고 노력하고있어; 내 누출이 있거나 입력 스트림에 RAM을 차지하는 오버플로가있는 경우에도 마찬가지입니다. 내가 알아 내면 대답을 답으로 표시 할 것입니다. :) – Mateo

+0

고마워요. 다운로드하는 동안 flushedinputstream을 사용하여 비트 맵을 디코딩하는 데 램을 덜 사용했기 때문에 도움이되었습니다. 바이트를 다운로드하고 비트 맵으로 디코딩하는 대신 마침내 나는 주된 문제는 모든 작업이 동시에 다운로드되기 시작했다는 것을 알았지 만. 작업 수를 2 다운로드로 제한하고 다른 모든 대기열을 관리했습니다. 스레드를 업데이트하여 AsyncTask에 수정 사항을 넣어 이와 같은 작업을 할 것입니다. – Mateo