約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のソースを読んで、なぜこのように機能するのか理解できません-同時に実行されているタスクは1つだけです)。もう1つは、画像の大きなリストビュー(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はより高速に読み込まれますが、高速スクロールダウンにはまだ問題があります。そしてもう1つ-時々私はエラーを受け取ります:
java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)
私は本当に何をすべきかわかりません。addTaskとexecuteNextを同期させようとしましたが(同期に関する質問は得意ではありません)、それでも価値があります。メディアストアからIDで画像のサムネイルをダウンロードするのに役立つサードパーティのライブラリを知りません。多分あなたは私を助けることができますか?