Android開発記録雑記 -5ページ目

Android開発記録雑記

パソコン中級者ぐらいの私が
スマホアプリの開発をしてみようと思い立ち
Androidで動くアプリ開発過程をこれから書き記していきます。

今日は画像処理の2回目 Processing Bitmaps Off the UI Thread
です。

ビットマップのロードや表示プロセスは
メイン処理でなく別スレッドを立てて処理しましょう。
という内容です。

性能はよく知らないですが、スマホも4コアCPU搭載機
などが出てますから、有効な技術といえるでしょう。


では、まずスレッドの作成から
AsyncTask を使います。
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    private final WeakReference<ImageView> imageViewReference;
    private int data = 0;

    public BitmapWorkerTask(ImageView imageView) {
        imageViewReference = new WeakReference<ImageView>(imageView);
    }
バックグラウンド処理
    @Override
    protected Bitmap doInBackground(Integer... params) {
前回やったビットマップ読み込み処理
        data = params[0];
        return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
    }
バックグラウンド終了後処理
    @Override
    protected void onPostExecute(Bitmap bitmap) {
終了のタイミングでどうなってるかわからないので
全てのnullチェックを行う必要がある
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}

非同期処理の場合は新しいタスクに処理を丸投げします。
public void loadBitmap(int resId, ImageView imageView) {
    BitmapWorkerTask task = new BitmapWorkerTask(imageView);
    task.execute(resId);
}


さて、マルチスレッドの同期処理をビューに当てはめた場合

子ビューで開始された同期処理が関連付けられているビューがすでに
別の子ビューで使用するためにリサイクルされていないという保証はありません。
さらに、非同期タスクが開始される順序は、それらが完了した順序であるという
保証はありません。

要するに、いつどうなるか判らないので
常に状態をチェックして実行する必要があります。

ビットマッププレースホルダーの BitmapDrawable
最新のタスクを参照できるようにします。
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask>
bitmapWorkerTaskReference;

public AsyncDrawable(Resources res, Bitmap bitmap,
BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference =
new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}

public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}


非同期の場合でも参照をバインドさせます
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);
}
}


同一プロセスが実行中かチェックし
前のタスクならキャンセルさせる処理を追加
public static boolean cancelPotentialWork(int data, ImageView imageView){
final BitmapWorkerTask bitmapWorkerTask =
 getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final int bitmapData = bitmapWorkerTask.data;
if (bitmapData != data) {
bitmapWorkerTask.cancel(true);
} else {
return false;
}
}
return true;
}


タスク取得メソッド
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;
}



最後に onPostExecute にキャンセルチェックを追加
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...

@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);
}
}
}
}


とまあ、マルチタスク処理の際は処理する前に値のチェック
が必須になります。

本日は以上です。