7

最初の問題:

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

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

私はv4互換性パッケージを使用しているので、拡張するカスタムローダーを使用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を与えることで問題が解決するかもしれないと思いましたが、違いはないようです。その結果、私は一見ランダムな画像になってしまい、アプリは必要なものの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;
        }

    }

}
4

1 に答える 1

11

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

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

アプリに同じ画像ローダーコードを使用して、画像を読み込もうとするたびに非同期タスクをスレッドプールキューに配置します。グーグルの例では、彼らは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;

          }
    }

したがって、フラグメント内にある画像ビューを回転して、フラグメントが非表示になったときにキャンセルし、フラグメントが表示されたときに表示します。

于 2011-11-26T03:52:52.783 に答える