Androidアプリケーション開発における画像の非同期ロード

6019 ワード

Androidアプリケーション開発ではUIスレッドで時間のかかる操作はできません.そうしないと、煩わしいANRウィンドウがポップアップします.
アプリケーション開発では、ネットワーク、ディスク、または他の非メモリのピクチャリソースをロードする必要がある場合、ロード時間がディスク、ネットワーク、ピクチャサイズ、CPUなどの他の要因の影響を受けるため、時間のかかる操作が容易に発生します.そのため、UIスレッドでは同様の操作は避けなければならない.今日は、AsyncTaskを介して画像を非同期でロードする方法と、マルチスレッド同時問題をどのように処理するかについて説明します.
AsyncTaskを使用して画像をロードする方法
AysncTaskを使用すると、バックグラウンドスレッドを起動してリソースをロードし、UIスレッドに結果を戻すことができます.これを使用する場合は、AysncTaskおよびd e codeSampledBitmapFromResource()メソッドを使用してImageViewに大きな画像をロードする例を示します.
class BitmapWorkerTask extends AsyncTask {
    private final WeakReference imageViewReference;
    private int data = 0;
                                                                       
    public BitmapWorkerTask(ImageView imageView) {
        // Use a WeakReference to ensure the ImageView can be garbage collected
        imageViewReference = new WeakReference(imageView);
    }
                                                                                                                                                                                      
    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        data = params[0];
        return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
    }
                                                                                                                                                                                    
    // Once complete, see if ImageView is still around and set bitmap.
    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}

WeakReferenceを使用してImageViewを保存する理由は、メモリリソースが不足しているときにAsyncTaskがリソース回収を阻止しないことを確認するためです.したがって、taskが終了したときにImageviewが存在することを保証できないので、onPostExecuteで検証する必要があります(この例では、Taskが終了する前にユーザーがActivityをオフにしたり、システム設定が変更されたりした場合、ImageViewが回収される可能性があります).
画像を非同期でロードするには、次のようにします.
public void loadBitmap(int resId, ImageView imageView) {
     BitmapWorkerTask task = new BitmapWorkerTask(imageView);
     task.execute(resId);
}

同時処理の処理方法
一般的なViewコンポーネントではListViewやGridViewのような実用的なメモリを効率的に使用するために,ユーザがViewスクロール操作を行う際にサブビューを使用しないリソースを回収するシステムがあり,以上のようにピクチャロードを行う場合に別の問題が導入される.各サブViewでAsyncTaskをオンにすると、タスクの完了時に関連するViewが回収されたかどうかは保証されません.また、ロードが完了した順序を保証することはできません.
AsyncTaskの参照をImageView関連Drawableに保存することで、タスク完了時に参照が存在するかどうかを確認できます.
作業タスクスレッドの参照を格納する専用Drawableサブクラスを作成します.これにより、タスクの完了時にImageViewに画像を設定できます.
static class AsyncDrawable extends BitmapDrawable {
    private final WeakReference bitmapWorkerTaskReference;
    public AsyncDrawable(Resources res, Bitmap bitmap,
            BitmapWorkerTask bitmapWorkerTask) {
        super(res, bitmap);
        bitmapWorkerTaskReference =
            new WeakReference(bitmapWorkerTask);
    }
                                                                 
    public BitmapWorkerTask getBitmapWorkerTask() {
        return bitmapWorkerTaskReference.get();
    }
}

BitmapTaskを実行する前に、AsyncDrawableを作成してImageViewにバインドできます.
public void loadBitmap(int resId, ImageView imageView) {
    if (cancelPotentialWork(resId, imageView)) {
        final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
        final AsyncDrawable asyncDrawable =
                new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
        imageView.setImageDrawable(asyncDrawable);
        task.execute(resId);
    }
}

上記のコードではcancelPotentialWorkによって実行中のタスクバインドがImageViewに存在するかどうかを判断し、ある場合はタスクcancelメソッドを実行することによってキャンセルすることはよくありません.
次に、cancelPotentialWorkの実装を示します.
public static boolean cancelPotentialWork(int data, ImageView imageView) {
    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
              
    if (bitmapWorkerTask != null) {
        final int bitmapData = bitmapWorkerTask.data;
        if (bitmapData != data) {
            // Cancel previous task
            bitmapWorkerTask.cancel(true);
        } else {
            // The same work is already in progress
            return false;
        }
    }
    // No task associated with the ImageView, or an existing task was cancelled
    return true;
}

次に、ImageViewによって関連する非同期タスクを検索する補助方法を示します.
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
   if (imageView != null) {
       final Drawable drawable = imageView.getDrawable();
       if (drawable instanceof AsyncDrawable) {
           final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
           return asyncDrawable.getBitmapWorkerTask();
       }
    }
    return null;
}

次はBitmapWorkerTaskのonPostExecuteで更新を実行する必要があります.
まず、タスクがキャンセルされたかどうかを確認します.たとえば、後で関連付けられたImageView:
class BitmapWorkerTask extends AsyncTask {
    ...                                                                                                                                    
    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }                                                                                                                             
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            final BitmapWorkerTask bitmapWorkerTask =
                    getBitmapWorkerTask(imageView);
            if (this == bitmapWorkerTask && imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}

以上の方法で、ListView、GridView、またはサブview回収処理を有する他のコンポーネントで使用できます.
loadBitmapは、GirdViewのAdapterのgetViewメソッドで呼び出されるなど、ImageViewに画像を簡単に追加できます.