2011-11-03 14 views
7

まず問題:Android - スレッドプール戦略とそれを実装するためにLoaderを使用できますか?

  • 私はカスタマイズFragmentStatePagerAdapter内で複数のFragmentLists を使用するアプリケーションに取り組んでいます。 このようなフラグメントの潜在的にかなりの数は、20と40の間にある可能性があります。
  • 各フラグメントは、各アイテムがテキストまたはイメージを含むことができるリストです。
  • 利用可能な場合、フラグメントが画面の外になると画像が
(一時停止していない)任意のアップロードや現在の活動が取り消されるべきでSDにもウェブから非同期にアップロードされ、一時メモリキャッシュにキャッシュする必要があり、

私の最初の実装は、Googleのよく知られたimage loader codeに続いています。そのコードの私の問題は、基本的に画像ごとにAsyncTaskという1つのインスタンスを作成するということです。私の場合は本当に速くアプリを殺す。

私はv4互換のパッケージを使用していますので、AsyncTaskLoaderを拡張するカスタムローダーを使用すると、内部的にスレッドプールが実装されていると思います。しかし、このコードを複数回実行すると、私の不愉快な驚きにもかかわらず、それぞれの呼び出しがそれを中断します。このメソッドは見えてくる各リスト項目のためのループ内で実行される

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

:私は私のListView#getView方法でこれを持っていると言います。私が述べたように、それぞれの呼び出しは前の呼び出しを終了します。あるいは、少なくともそれは、その後、私は多分問題を助けます各ローダーに一意のIDを与えるが、すべての違いを確認していないようだと思った

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 

LogCat

に基づいて起こるものです。結果として、私は一見無作為の画像で終わり、アプリは私が必要とするものの1/4までを読み込むことはありません。

私がやりたいローダーを修正する方法だろうどのような質問

  • (および方法はありますか?)
  • AsyncTaskプールを作成するための良い方法であるとされていない場合おそらくそれの実装を実装している?

実際のダウンロード/保存ロジックが別の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レベル11)をターゲットにしている場合は、新しく追加されたAPI [AsyncTask.executeOnExecutor()](http://developer.android.com/reference/android/os/AsyncTask.html#executeOnExecutor% 28Java.util.concurrent.Executor、%20Params ...%29)AsyncTask作成ライフサイクルでスレッドプールを細かく制御します。 – yorkw

+0

それにもかかわらず、AsynkTaskは1回しか実行できないので、イメージごとに1つのインスタンスを作成する必要があります。それを60にするとそれは多くのオブジェクトです – Bostone

答えて

11

だから最初のものが最初です。アンドロイドに付属しているAsyncTaskは、あなたのアプリを捨てたり、クラッシュさせるべきではありません。 AsyncTasksは、スレッドプール内で実行されます。スレッドプールでは、実際に同時に実行されるスレッドは最大で5つです。実行する多くのタスクをキューに入れることができますが、一度に実行するのは5つだけです。これらをバックグラウンドスレッドプールで実行することで、アプリケーションに何も影響を与えてはならず、スムーズに実行する必要があります。

AsyncTaskローダーのパフォーマンスに不満がある場合、AsyncTaskLoaderを使用しても問題は解決しません。 AsyncTaskLoaderはローダインタフェースを受け取り、それをAsyncTaskと結びつけるだけです。つまり、基本的にonLoadFinished - > onPostExecute、onStart - > onLoadInBackgroundのマッピングです。それはまったく同じことです。

イメージをロードしようとするたびに、スレッドプールキューにasynctaskを置くアプリケーションと同じイメージローダーコードを使用します。 Googleの例では、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の実装です。 threadingが行われる限り、AsyncTaskにまだ存在しない* nothing *が追加されます。何かをキャンセルする必要があるときは、AsyncTaskをキャンセルしてAsyncTaskを使って自分自身で行うことができます。 – hackbod

+0

Dianneがそう言っていればいいのです:)私はまだ複数のタスクが待ち行列に入れられていないが、代わりに最後のものだけが残っているという振る舞いを見ています。それは正しい行動ですか? – Bostone

関連する問題