2

からバックグラウンドでビットマップをロードしていassets/ます。ネットでいくつかの解決策を見つけましたが、それらはすべての画像に対して新しいバックグラウンド タスクを開始します。

よりわかりやすいと思うので、Producer/Consumer パターンを持つロード スレッドが 1 つだけになるように変更しました。BlockingQueueUI スレッドからの要求とpublishProgress応答にsを使用します。

私のソリューションは正常に機能しますが、マルチスレッドを使用した方が良いかどうか疑問に思いました。私のコードはリクエストの無効化をサポートしていますが、新しいリクエストを処理する前に現在実行中の読み込みを常に終了します。

一方、スレッドはディスクアクセスをめぐって競合しませんか?

私のコード:

package org.example;

import android.content.ContextWrapper;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import android.os.AsyncTask;

import android.util.Pair;
import android.widget.ImageView;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.PriorityBlockingQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class ImageLoader extends AsyncTask<Void, Pair<ImageLoader.SetImageTask, Bitmap>, Void> {

    //~ Static fields/initializers -------------------------------------------------------------------------------------

    private static final Logger L = LoggerFactory.getLogger(ImageLoader.class);

    //~ Instance fields ------------------------------------------------------------------------------------------------

    private final Map<String, Bitmap> cache                 = Maps.newHashMap();
    private final ContextWrapper contextWrapper;
    private final PriorityBlockingQueue<LoadTask> loadTasks =
        new PriorityBlockingQueue<ImageLoader.LoadTask>(1, Ordering.arbitrary());

    /* is changed to invalidate tasks */
    private String uuid = null;

    //~ Constructors ---------------------------------------------------------------------------------------------------

    public ImageLoader(final ContextWrapper contextWrapper) {
        this.contextWrapper     = contextWrapper;
        this.uuid               = UUID.randomUUID().toString();
    }

    //~ Methods --------------------------------------------------------------------------------------------------------

    public void invalidateTasks() {
        L.debug("invalidating tasks");
        this.uuid = UUID.randomUUID().toString();
    }

    public void setImage(final ImageView view, final String asset) {
        this.loadTasks.add(new SetImageTask(this.uuid, view, asset));
    }

    public void loadIntoCache(final ImmutableList<String> assets) {
        for (final String asset : assets) {
            this.loadTasks.add(new LoadIntoCacheTask(asset));
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    protected Void doInBackground(Void... param) {
        try {
            while (true) {

                LoadTask task = this.loadTasks.take();
                if (task instanceof SetImageTask) {

                    SetImageTask setImageTask = (SetImageTask) task;
                    if (setImageTask.uuid.equals(this.uuid)) {

                        Bitmap bitmap = this.loadAsset(setImageTask.asset);
                        if (bitmap != null) {
                            this.publishProgress(Pair.create(setImageTask, bitmap));
                        }
                    } else {
                        L.debug("request for asset '{}' outdated", setImageTask.asset);
                    }
                } else {

                    LoadIntoCacheTask cacheTask = (LoadIntoCacheTask) task;
                    Bitmap bitmap               = this.loadAsset(cacheTask.asset);
                    L.debug("storing in cache: '{}'", cacheTask.asset);
                    cache.put(cacheTask.asset, bitmap);
                }
            }
        } catch (final InterruptedException e) {
            L.debug("interrupted -> exiting");
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(Pair<SetImageTask, Bitmap>... results) {

        Pair<SetImageTask, Bitmap> result = results[0];
        if (result.first.uuid.equals(this.uuid)) {
            result.first.view.setImageBitmap(result.second);
        } else {
            L.debug("request for asset '{}' outdated", result.first.asset);
        }
    }

    private Bitmap loadAsset(final String asset) {
        if (this.cache.containsKey(asset)) {
            L.debug("serving from cache '{}'", asset);
            return this.cache.get(asset);

        } else {
            L.debug("loading from assets: '{}'", asset);

            InputStream in = null;
            Bitmap bitmap  = null;
            try {
                in         = new BufferedInputStream(contextWrapper.getAssets().open(asset));
                bitmap     = BitmapFactory.decodeStream(in);
            } catch (final IOException e) {
                L.error(e.getMessage(), e);
            } finally {
                try {
                    in.close();
                } catch (final IOException e) {}
            }

            return bitmap;
        }
    }

    //~ Inner Interfaces -----------------------------------------------------------------------------------------------

    protected static interface LoadTask {}

    //~ Inner Classes --------------------------------------------------------------------------------------------------

    protected static final class SetImageTask implements LoadTask {

        private final String uuid;
        private final ImageView view;
        private final String asset;

        public SetImageTask(final String uuid, final ImageView view, final String asset) {
            this.uuid      = uuid;
            this.view      = view;
            this.asset     = asset;
        }
    }

    protected static final class LoadIntoCacheTask implements LoadTask {

        private final String asset;

        public LoadIntoCacheTask(final String asset) {
            this.asset = asset;
        }
    }
}
4

0 に答える 0