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

Android開発記録雑記

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

今日は前回の続き Caching Bitmaps
からの続きをやっていきます。

今回はディスクキャッシュを使用します。

メモリーキャッシュと比べて容量は大きくなりますが
アクセス速度は落ちてしまいます。

前回のメモリーキャッシュに
ディスクキャッシュへの対応を追加します。
private DiskLruCache mDiskLruCache;
private final Object mDiskCacheLock = new Object();
private boolean mDiskCacheStarting = true;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "thumbnails";

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
バックグラウンドでディスクキャッシュを初期化
    File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);
    new InitDiskCacheTask().execute(cacheDir);
    ...
}


class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
    @Override
    protected Void doInBackground(File... params) {
        synchronized (mDiskCacheLock) {
            File cacheDir = params[0];
            mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
            mDiskCacheStarting = false;
            mDiskCacheLock.notifyAll();
        }
        return null;
    }
}

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    ...
    @Override
    protected Bitmap doInBackground(Integer... params) {
        final String imageKey = String.valueOf(params[0]);
ディスクキャッシュにビットマップがあるかチェック
        Bitmap bitmap = getBitmapFromDiskCache(imageKey);
        if (bitmap == null) {
無いならロードする
            final Bitmap bitmap = decodeSampledBitmapFromResource(
                    getResources(), params[0], 100, 100));
        }
キャッシュに追加する
        addBitmapToCache(imageKey, bitmap);
        return bitmap;
    }
    ...
}

public void addBitmapToCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
ディスクキャッシュに追加
    synchronized (mDiskCacheLock) {
        if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
            mDiskLruCache.put(key, bitmap);
        }
    }
}

public Bitmap getBitmapFromDiskCache(String key) {
    synchronized (mDiskCacheLock) {
バックグランド処理を待つ
        while (mDiskCacheStarting) {
            try {
                mDiskCacheLock.wait();
            } catch (InterruptedException e) {}
        }
        if (mDiskLruCache != null) {
            return mDiskLruCache.get(key);
        }
    }
    return null;
}

キャッシュディレクトリの取得
public static File getDiskCacheDir(Context context, String uniqueName) {
外部ストレージがあればそちらを無ければ内部キャッシュディレクトリを取得
    final String cachePath =
            Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
                    !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
                            context.getCacheDir().getPath();

    return new File(cachePath + File.separator + uniqueName);
}

この実装では、ロックオブジェクトは、キャッシュが初期化されるまでの
アプリはディスクキャッシュから読み込まれないことが保証されます。


次に、画面を縦横回転などした場合
画面は一度破壊されてまた再構築されるわけですが
この時ビットマップも再構築されてしまうので
処理に遅延が発生するかもしれません。

で、せっかくキャッシュ機能を作ったのでこれを生かして
再構築されてもキャッシュ内容はそのままで済む
という方法があります。

フラグメントの setRetainInstance(true) メソッドを使います。
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    RetainFragment mRetainFragment =
            RetainFragment.findOrCreateRetainFragment(getFragmentManager());
    mMemoryCache = RetainFragment.mRetainedCache;
    if (mMemoryCache == null) {
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            ...
        }
        mRetainFragment.mRetainedCache = mMemoryCache;
    }
    ...
}

class RetainFragment extends Fragment {
    private static final String TAG = "RetainFragment";
    public LruCache<String, Bitmap> mRetainedCache;

    public RetainFragment() {}

    public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
        RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
        if (fragment == null) {
            fragment = new RetainFragment();
        }
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
インスタンスが活動再作成を越えて保持される
        setRetainInstance(true);
    }
}


本日は以上です。