从MediaStore异步下载图像到gridview

时间:2012-12-18 07:33:53

标签: android listview image-processing asynchronous mediastore

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

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同步(我不擅长有关同步的问题),但它的工作甚至值得。我不知道任何第三方库有助于从媒体商店下载图片缩略图。也许你可以帮助我?

1 个答案:

答案 0 :(得分:0)

非常感谢this示例。我已经修改了一下使用MediaStore和LruCache的拇指。它现在工作得很好。