0

我有大约 1500 张图像的网格视图。我正在使用 MediaStore 中的缩略图来显示它们。但是当我在 UI 线程中填充网格时,它第一次工作得太慢了(还没有创建缩略图?)。所以我决定把这个过程移到后台。使用本教程和另一个我编写了这个类:

public class ImageLoader {
// -----------------------------------------------------------------------
//
// Fields
//
// -----------------------------------------------------------------------
private static Context mContext;
private static ImageLoader mImageLoader = new ImageLoader();
// -----------------------------------------------------------------------
//
// Static methods
//
// -----------------------------------------------------------------------
public static void init(Application application){
    mContext = application;
}

public static ImageLoader getInstance(){
    return mImageLoader;
}

// -----------------------------------------------------------------------
//
// Methods
//
// -----------------------------------------------------------------------
public void download(ImageView imageView, final long id) {
    Boolean b = cancelPotentialDownload(id, imageView);
    Log.v("Loader", "download " + b.toString());
    if (b) {
        DownloadImageTask task = new DownloadImageTask(imageView, id);
        DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
        imageView.setImageDrawable(downloadedDrawable);
        task.execute();
    }
}

// -----------------------------------------------------------------------
//
// Inner classes
//
// -----------------------------------------------------------------------
private class DownloadImageTask extends AsyncTask<Void, Void, Bitmap>{
    private final WeakReference<ImageView> iv;
    private final long id;

    public DownloadImageTask(ImageView imageView, long id){
        iv = new WeakReference<ImageView>(imageView);
        this.id = id;
    }

    @Override
    protected Bitmap doInBackground(Void... params) {
        Bitmap bitmap = downloadImageThumbnail(id);
        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap result) {
         Log.v("Task", "completed " + (result == null)); 
         if (iv != null) {
                ImageView imageView = iv.get();
                DownloadImageTask bitmapDownloaderTask = getDownloadImageTask(imageView);
                // Change bitmap only if this process is still associated with it
                if (this == bitmapDownloaderTask) {
                    imageView.setImageBitmap(result);
                }
            }
    }
}

static class DownloadedDrawable extends ColorDrawable {
    private final WeakReference<DownloadImageTask> downloadImageTaskReference;

    public DownloadedDrawable(DownloadImageTask downloadImageTask) {
        super(Color.TRANSPARENT);
        downloadImageTaskReference = new WeakReference<DownloadImageTask>(downloadImageTask);
    }

    public DownloadImageTask getDownloadImageTask() {
        return downloadImageTaskReference.get();
    }
}

// -----------------------------------------------------------------------
//
// Private static helper methods
//
// -----------------------------------------------------------------------
private static boolean cancelPotentialDownload(long id, ImageView imageView) {
    DownloadImageTask downloadImageTask = getDownloadImageTask(imageView);

    if (downloadImageTask != null) {
        long imageId = downloadImageTask.id;
        if ((imageId == 0) || (imageId != id)) {
            Boolean b = downloadImageTask.cancel(true);
            Log.v("Task", "Stopped " + b.toString());
        } else {
            // The same URL is already being downloaded.
            return false;
        }
    }
    return true;
}

private static DownloadImageTask getDownloadImageTask(ImageView imageView) {
    if (imageView != null) {
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof DownloadedDrawable) {
            DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
            return downloadedDrawable.getDownloadImageTask();
        }
    }
    return null;
}
// -----------------------------------------------------------------------
//
// Private helper methods
//
// -----------------------------------------------------------------------
private Bitmap downloadImageThumbnail(long id) {
    Bitmap bitmap = MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), id, MediaStore.Images.Thumbnails.MICRO_KIND, null);
    return bitmap;
}

}

但是这么多的图像存在一个问题 - 有很多任务同时运行,所以在活动开始后有一个空白的网格视图,然后突然它变得充满了图像(我已经阅读了 AsyncTask 源并没有'不明白为什么它会这样工作——同时只有一个任务在运行)。还有一个——如果你快速向下滚动一个大的图像列表视图(1500),那么你需要等待太长时间才能看到结果。无论如何,我需要使 gridview 顺利填充。所以我决定自己管理任务并做了一些修改:

private static final int POOL_CAPACITY = 10;
// -----------------------------------------------------------------------
//
// Fields
//
// -----------------------------------------------------------------------
private static Context mContext;
private static ImageLoader mImageLoader = new ImageLoader();


private static ArrayList<DownloadImageTask> executingPool = new ArrayList<ImageLoader.DownloadImageTask>();
private static ArrayList<DownloadImageTask> waitingPool = new ArrayList<ImageLoader.DownloadImageTask>();
// -----------------------------------------------------------------------
//
// Static methods
//
// -----------------------------------------------------------------------
public static void init(Application application){
    mContext = application;
}

public static ImageLoader getInstance(){
    return mImageLoader;
}

// -----------------------------------------------------------------------
//
// Methods
//
// -----------------------------------------------------------------------
public void download(ImageView imageView, final long id) {
    Boolean b = cancelPotentialDownload(id, imageView);
    Log.v("Loader", "download " + b.toString());
    if (b) {
        DownloadImageTask task = new DownloadImageTask(imageView, id);
        Log.v("Loader","download " + task.hashCode() + " ex size " + executingPool.size() + " wa size " + waitingPool.size());
        DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
        imageView.setImageDrawable(downloadedDrawable);
        addTask(task);
    }
}

private void addTask(DownloadImageTask task){
    Log.v("Loader", "addTask " + task.hashCode() + " ex size " + executingPool.size() + " wa size " + waitingPool.size());
    if(executingPool.size() < POOL_CAPACITY) {
        executingPool.add(task);
        task.execute();
    }
    else
        waitingPool.add(task);
}

private synchronized void executeNext(DownloadImageTask finished){
    Log.v("Loader", "executeNext " + finished.hashCode() + " ex size " + executingPool.size() + " wa size " + waitingPool.size());
    executingPool.remove(finished);
    if(waitingPool.size() > 0){
        DownloadImageTask task = waitingPool.remove(0);
        addTask(task);
    }
}
// -----------------------------------------------------------------------
//
// Inner classes
//
// -----------------------------------------------------------------------
private class DownloadImageTask extends AsyncTask<Void, Void, Bitmap>{
    private final WeakReference<ImageView> iv;
    private final long id;

    public DownloadImageTask(ImageView imageView, long id){
        iv = new WeakReference<ImageView>(imageView);
        this.id = id;
    }

    @Override
    protected Bitmap doInBackground(Void... params) {
        Log.v("Loader", "doInBackground " + this.hashCode() + " ex size " + executingPool.size() + " wa size " + waitingPool.size());
        Bitmap bitmap = downloadImageThumbnail(id);
        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap result) {
         Log.v("Task", "completed " + (result == null)); 
         if (iv != null) {
                ImageView imageView = iv.get();
                DownloadImageTask bitmapDownloaderTask = getDownloadImageTask(imageView);
                // Change bitmap only if this process is still associated with it
                if (this == bitmapDownloaderTask) {
                    imageView.setImageBitmap(result);
                }
            }
         executeNext(this);
    }
}

static class DownloadedDrawable extends ColorDrawable {
    private final WeakReference<DownloadImageTask> downloadImageTaskReference;

    public DownloadedDrawable(DownloadImageTask downloadImageTask) {
        super(Color.TRANSPARENT);
        downloadImageTaskReference = new WeakReference<DownloadImageTask>(downloadImageTask);
    }

    public DownloadImageTask getDownloadImageTask() {
        return downloadImageTaskReference.get();
    }
}

// -----------------------------------------------------------------------
//
// Private static helper methods
//
// -----------------------------------------------------------------------
private static boolean cancelPotentialDownload(long id, ImageView imageView) {
    DownloadImageTask downloadImageTask = getDownloadImageTask(imageView);

    if (downloadImageTask != null) {
        long imageId = downloadImageTask.id;
        if ((imageId == 0) || (imageId != id)) {
            Boolean b = downloadImageTask.cancel(true);
            Log.v("Task", "Stopped " + b.toString());
        } else {
            // The same URL is already being downloaded.
            return false;
        }
    }
    return true;
}

private static DownloadImageTask getDownloadImageTask(ImageView imageView) {
    if (imageView != null) {
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof DownloadedDrawable) {
            DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
            return downloadedDrawable.getDownloadImageTask();
        }
    }
    return null;
}
// -----------------------------------------------------------------------
//
// Private helper methods
//
// -----------------------------------------------------------------------
private Bitmap downloadImageThumbnail(long id) {
    Bitmap bitmap = MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), id, MediaStore.Images.Thumbnails.MICRO_KIND, null);
    return bitmap;
}

}

现在 gridview 填充得更快,但快速向下滚动仍然存在问题。还有一个——有时我得到一个错误:

java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)

我真的不知道该怎么办。我试图使 addTask 和 executeNext 同步(我不擅长关于同步的问题)但它甚至值得。我不知道有任何第三方库可以帮助通过它的 id 从 mediastore 下载图像缩略图。也许你能帮助我?

4

1 回答 1

0

非常感谢这个例子。我已经对其进行了一些修改,以使用来自 MediaStore 和 LruCache 的拇指。现在效果很好。

于 2012-12-18T11:19:07.410 回答