我有大约 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 下载图像缩略图。也许你能帮助我?