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

Android開発記録雑記

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

今日は Managing Bitmap Memory
を進めます。

大量の画像データの中から頻度の低いもの解放して
メモリーを有効利用する方法です。

Androidのバージョンによって
仕様が異なるので対処を分けます。


バージョン2.3.3(APIレベル10)以下の場合

OutOfMemoryError エラーを避ける為に
使用頻度の低いものを recycle() します。

private int mCacheRefCount = 0;
private int mDisplayRefCount = 0;
...
表示状態の変化
public void setIsDisplayed(boolean isDisplayed) {
    synchronized (this) {
        if (isDisplayed) {
            mDisplayRefCount++;
            mHasBeenDisplayed = true;
        } else {
            mDisplayRefCount--;
        }
    }
    checkState();
}
キャッシュ状態の変化
public void setIsCached(boolean isCached) {
    synchronized (this) {
        if (isCached) {
            mCacheRefCount++;
        } else {
            mCacheRefCount--;
        }
    }
    checkState();
}

private synchronized void checkState() {
表示も参照もされなくなったらリサイクル
    if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
            && hasValidBitmap()) {
        getBitmap().recycle();
    }
}

private synchronized boolean hasValidBitmap() {
    Bitmap bitmap = getBitmap();
    return bitmap != null && !bitmap.isRecycled();
}



アンドロイド3.0(APIレベル11)以上の場合


BitmapFactory.Options.inBitmap の設定を行います。
そうするとコンテンツをロードするときに
既存のビットマップを再利用しようとします。

HashSet<SoftReference<Bitmap>> mReusableBitmaps;
private LruCache<String, BitmapDrawable> mMemoryCache;
Android対応バージョンチェック
if (Utils.hasHoneycomb()) {
    mReusableBitmaps = new HashSet<SoftReference<Bitmap>>();
}

mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {
    @Override
    protected void entryRemoved(boolean evicted, String key,
            BitmapDrawable oldValue, BitmapDrawable newValue) {
        if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
メモリーキャッシュから取り除かれたと通知します
            ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
        } else {
            if (Utils.hasHoneycomb()) {
再利用の為セット
                mReusableBitmaps.add
                        (new SoftReference<Bitmap>(oldValue.getBitmap()));
            }
        }
    }
....
}


ビットマップロードの際再利用可能かどうかを追加
public static Bitmap decodeSampledBitmapFromFile(String filename,
        int reqWidth, int reqHeight, ImageCache cache) {
    final BitmapFactory.Options options = new BitmapFactory.Options();
    ...
    BitmapFactory.decodeFile(filename, options);
    ...
    if (Utils.hasHoneycomb()) {
        addInBitmapOptions(options, cache);
    }
    ...
    return BitmapFactory.decodeFile(filename, options);
}
再利用可能なビットマップをセット
private static void addInBitmapOptions(BitmapFactory.Options options,
        ImageCache cache) {
    options.inMutable = true;
    if (cache != null) {
        Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
        if (inBitmap != null) {
            options.inBitmap = inBitmap;
        }
    }
}
再利用可能な基準を満たすかチェック
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
        Bitmap bitmap = null;
    if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
        final Iterator<SoftReference<Bitmap>> iterator
                = mReusableBitmaps.iterator();
        Bitmap item;

        while (iterator.hasNext()) {
            item = iterator.next().get();
            if (null != item && item.isMutable()) {
                if (canUseForInBitmap(item, options)) {
                    bitmap = item;
                    iterator.remove();
                    break;
                }
            } else {
                iterator.remove();
            }
        }
    }
    return bitmap;
}
サイズの一致チェック
private static boolean canUseForInBitmap(
        Bitmap candidate, BitmapFactory.Options targetOptions) {
    int width = targetOptions.outWidth / targetOptions.inSampleSize;
    int height = targetOptions.outHeight / targetOptions.inSampleSize;

    return candidate.getWidth() == width && candidate.getHeight() == height;
}

本日は以上です。