2011-11-03 36 views
7

第一問題:Android - 線程池策略和可以用Loader來實現它嗎?

  • 我正在使用自定義FragmentStatePagerAdapter內的多個FragmentLists 應用。可能有, 潛在大量的這樣的片段說20至40之間。
  • 每個片段是一個列表,其中每個項目可能包含文本或圖像。
  • 的圖像需要從網絡異步上傳並緩存到臨時內存緩存,併爲SD(如果可用)
  • 當片段熄滅屏幕任意上傳和當前活動應取消(不暫停)

我的第一個實現遵循Google公司的image loader code。我的代碼問題是,它基本上爲每個圖像創建一個AsyncTask的實例。在我的情況下,殺死應用程序真的很快。

由於我使用的是v4兼容包,我認爲使用定製的Loader擴展AsyncTaskLoader會幫助我,因爲它在內部實現了一個線程池。但是,如果我多次執行此代碼,則每次後續調用都會中斷以前的操作,令我感到不愉快的驚喜。說我有這在我的ListView#getView方法:

getSupportLoaderManager().restartLoader(0, args, listener); 

這種方法是在循環對於映入眼簾的每個列表項目執行。正如我所說 - 每個以後的調用將終止前一個。或者至少這是發生什麼基礎上的logcat

11-03 13:33:34.910: V/LoaderManager(14313): restartLoader in LoaderManager: args=Bundle[{URL=http://blah-blah/pm.png}] 
11-03 13:33:34.920: V/LoaderManager(14313): Removing pending loader: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}} 
11-03 13:33:34.920: V/LoaderManager(14313): Destroying: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}} 
11-03 13:33:34.920: V/LoaderManager(14313): Enqueuing as new pending loader 

然後我想,也許給唯一的ID給每個裝載機將有助於問題,但它似乎並沒有任何區別。結果,我看起來隨機的圖像和應用程序永遠不會加載我所需要的四分之一。

問題

  • 什麼是修復裝載機做我想做什麼(有沒有辦法?)
  • 的方式,如果沒有什麼是創造AsyncTask池的好辦法,是那裏可能正在執行它?

爲了讓您知道這裏的代碼是精簡版的Loader,其中實際的下載/保存邏輯位於單獨的ImageManager類中。

public class ImageLoader extends AsyncTaskLoader<TaggedDrawable> { 
     private static final String TAG = ImageLoader.class.getName(); 
     /** Wrapper around BitmapDrawable that adds String field to id the drawable */ 
     TaggedDrawable img; 
     private final String url; 
     private final File cacheDir; 
     private final HttpClient client; 


    /** 
    * @param context 
    */ 
    public ImageLoader(final Context context, final String url, final File cacheDir, final HttpClient client) { 
     super(context); 
     this.url = url; 
     this.cacheDir = cacheDir; 
     this.client = client; 
    } 

    @Override 
    public TaggedDrawable loadInBackground() { 
     Bitmap b = null; 
     // first attempt to load file from SD 
     final File f = new File(this.cacheDir, ImageManager.getNameFromUrl(url)); 
     if (f.exists()) { 
      b = BitmapFactory.decodeFile(f.getPath()); 
     } else { 
      b = ImageManager.downloadBitmap(url, client); 
      if (b != null) { 
       ImageManager.saveToSD(url, cacheDir, b); 
      } 
     } 
     return new TaggedDrawable(url, b); 
    } 

    @Override 
    protected void onStartLoading() { 
     if (this.img != null) { 
      // If we currently have a result available, deliver it immediately. 
      deliverResult(this.img); 
     } else { 
      forceLoad(); 
     } 
    } 

    @Override 
    public void deliverResult(final TaggedDrawable img) { 
     this.img = img; 
     if (isStarted()) { 
      // If the Loader is currently started, we can immediately deliver its results. 
      super.deliverResult(img); 
     } 
    } 

    @Override 
    protected void onStopLoading() { 
     // Attempt to cancel the current load task if possible. 
     cancelLoad(); 
    } 

    @Override 
    protected void onReset() { 
     super.onReset(); 
     // Ensure the loader is stopped 
     onStopLoading(); 
     // At this point we can release the resources associated with 'apps' 
     // if needed. 
     if (this.img != null) { 
      this.img = null; 
     } 

    } 

} 
+0

'AsyncTask'已經使用了一個池。該池可以達到128個線程IIRC,這可能是您難度的來源。你可以使用'java.util.concurrent'類來實現你自己的線程池。 – CommonsWare

+1

如果您的開發針對Android 3.0(API Level 11),則可以使用新添加的API [AsyncTask.executeOnExecutor()](http://developer.android.com/reference/android/os/AsyncTask.html#executeOnExecutor% 28java.util.concurrent.Executor,%20Params ...%29)可以很好地控制你的AsyncTask創建生命週期的線程池。 – yorkw

+0

AsynkTask不過只能執行一次,所以我需要爲每個加載的圖像創建一個實例。使它60,這是很多的對象 – Bostone

回答

11

好吧,首先要做的第一件事。 Android附帶的AsyncTask不應該淹沒你的應用程序或導致它崩潰。 AsyncTasks在一個線程池中運行,最多有5個線程同時執行。雖然可以排隊執行許多任務,但一次只能執行其中的5個任務。通過在後臺線程池中執行它們,它們不應該對您的應用程序產生任何影響,它們應該運行平穩。

如果您對AsyncTask加載器性能不滿意,使用AsyncTaskLoader並不能解決您的問題。 AsyncTaskLoader只需要加載器接口並將其與AsyncTask結合。所以它基本上映射onLoadFinished - > onPostExecute,onStart - > onLoadInBackground。所以它是一樣的確切的事情。

我們對我們的應用程序使用相同的圖像加載器代碼,每當我們嘗試加載圖像時,都會導致將asynctask放到線程池隊列中。在谷歌的例子中,他們將imageview與其異步任務相關聯,以便他們可以取消異步任務,如果他們嘗試在某種適配器中重新使用imageview。你應該在這裏採取類似的策略。您應該將imageview與異步任務關聯在後臺加載圖像。當你有一個沒有顯示的片段時,你可以循環瀏覽與該片段相關的圖像視圖並取消加載任務。簡單地使用AsyncTask.cancel()應該工作得很好。

你還應該嘗試實現異步圖像視圖示例闡述的簡單圖像緩存機制。我們只需創建一個靜態哈希映射,它來自url - > weakreference。通過這種方式,圖像可以在需要時回收,因爲它們只能以弱引用持有。

這裏的圖像加載的輪廓,我們做

public class LazyLoadImageView extends ImageView { 
     public WeakReference<ImageFetchTask> getTask() { 
     return task; 
    } 

    public void setTask(ImageFetchTask task) { 
     this.task = new WeakReference<ImageFetchTask>(task); 
    } 

    private WeakReference<ImageFetchTask> task; 

     public void loadImage(String url, boolean useCache, Drawable loadingDrawable){ 

     BitmapDrawable cachedDrawable = ThumbnailImageCache.getCachedImage(url); 
     if(cachedDrawable != null){ 
      setImageDrawable(cachedDrawable); 
      cancelDownload(url); 
      return; 
     } 

     setImageDrawable(loadingDrawable); 

     if(url == null){ 
      makeDownloadStop(); 
      return; 
     } 

     if(cancelDownload(url)){ 
      ImageFetchTask task = new ImageFetchTask(this,useCache); 
      this.task = new WeakReference<ImageFetchTask>(task); 
      task.setUrl(url); 
      task.execute(); 
     } 


     ...... 

     public boolean cancelDownload(String url){ 

     if(task != null && task.get() != null){ 

      ImageFetchTask fetchTask = task.get(); 
      String downloadUrl = fetchTask.getUrl(); 

      if((downloadUrl == null) || !downloadUrl.equals(url)){ 
       fetchTask.cancel(true); 
       return true; 
      } else 
       return false; 
     } 

     return true; 

      } 
    } 

因此,只要通過在你的片段的圖像視圖旋轉,然後取消他們當你的片段隱藏和顯示他們當你的片段可見。

+0

這是鬆散的,我已經使用的策略,雖然我使用ModernAsyncTask與自定義池,弱引用和取消未顯示的上傳。但我認爲Loader不僅僅是AsyncTask的簡單重用。例如,我可以看到,如果我連續執行多個執行,則後一個調用將取消前一個執行。我想我將不得不花費一個晚上,只是設置示例項目並通過代碼來充分了解加載程序的工作原理 – Bostone

+2

加載程序不會執行任何操作。 AsyncTaskLoader是使用AsyncTask的Loader實現。就線程而言,它增加了* AsyncTask中尚未包含的* Nothing。當它需要取消某些東西時,它只會取消AsyncTask,您可以使用AsyncTask來完成自己的任務。 – hackbod

+0

好吧,如果Dianne說的那麼:)說 - 我仍然看到多個任務沒有排隊但取消而只剩下最後一個的行爲。這是正確的行爲? – Bostone

相關問題