ビットマップ管理、ピクチャダウンロードキャッシュ、ピクチャメモリの管理(五)ビットマップメモリの管理



ビットマップメモリの管理
「キャッシュビットマップ」で説明した手順に加えて、ゴミ回収とビットマップの再利用を促進するために使用できることもあります.推奨されるポリシーはandroidターゲットバージョンに依存します.次のBitmapFunインスタンスアプリケーションには、異なるandroidバージョンでアプリケーションをより効率的に実行する方法を示すクラスが含まれています.
この節の基礎として、android 2における以降のビットマップメモリ管理の改善に関する知識を以下に示す.2以前にごみ回収イベントが発生した場合,アプリケーションスレッドが停止し,操作が遅延する.android2.3コンカレントのゴミ回収処理が追加され、これはビットマップが適用されないとすぐに回収されることを意味する.android 2.3.3以前は、ピクセルデータのサポートはローカルメモリに保存されていました.これはビットマップ自体とは分離され、ビットマップ自体がdalvikスタックに存在し、ローカルメモリの画素データが予知できない方法で解放され、アプリケーションが短時間でメモリを消費してクラッシュする可能性があります.android 3から0から、画素データもその関連ビットマップとともにdalvikスタックに格納される.次に、異なるandroidバージョンでビットマップメモリ管理を最適化する方法について説明します.
        Android2.3.3までにandroid 2を管理する.3.3まではrecycle()をお勧めします.アプリケーションに大量のビットマップデータを表示する場合は、メモリ漏洩の問題が発生する可能性があります.recycle()メソッドでは、appがすぐにメモリを回収できます.
ビットマップが使用されないと判断した場合は、recycle()メソッドを簡単に呼び出すことができます.recycle()メソッドを呼び出し、ビットマップを描画しようとすると、「Canvas:try to use a recycled bitmap」が表示されます.
次のコードクリップは、参照カウント(パラメータ内のmDisplayRefCountおよびmCacheRefCount)を使用して、現在のビットマップが表示されているかメモリに存在しているかを追跡するrecycle()を呼び出す例を示します.次の条件を満たすと、コードはビットマップを回収します.mDisplayRefCacheとmCacheRefCacheはいずれも0である.ビットマップはnullではない、recycle()によって回収されていない.
private int mCacheRefCount = 0;
private int mDisplayRefCount = 0;
...
// Notify the drawable that the displayed state has changed.
// Keep a count to determine when the drawable is no longer displayed.
public void setIsDisplayed(boolean isDisplayed) {
    synchronized (this) {
        if (isDisplayed) {
            mDisplayRefCount++;
            mHasBeenDisplayed = true;
        } else {
            mDisplayRefCount--;
        }
    }
    // Check to see if recycle() can be called.
    checkState();
}

// Notify the drawable that the cache state has changed.
// Keep a count to determine when the drawable is no longer being cached.
public void setIsCached(boolean isCached) {
    synchronized (this) {
        if (isCached) {
            mCacheRefCount++;
        } else {
            mCacheRefCount--;
        }
    }
    // Check to see if recycle() can be called.
    checkState();
}

private synchronized void checkState() {
    // If the drawable cache and display ref counts = 0, and this drawable
    // has been displayed, then recycle.
    if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
            && hasValidBitmap()) {
        getBitmap().recycle();
    }
}

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

        android3.0以降のメモリ管理Android 3.0 BitmapFactoryを紹介しました.Options.inBitmapプロパティは、このプロパティが設定されている場合、Optionsオブジェクトを運ぶ復号方法は、コンテンツのロード時に既存のビットマップを再利用しようとします.これは、ビットマップのメモリが再利用され、操作性が向上し、メモリ割り当てが除去され、割り当てが解除されることを意味する.ただし、inBitmapの使用にはいくつかの制限があります.特にandroid 4.4では同じサイズのbitmaのみが再利用可能である.(詳細はinBitmapドキュメントを参照してください)
次のコードクリップを使用するためにビットマップを保存するには、インスタンスアプリケーションに既存のビットマップを保存して、後続の使用に備える方法を示します.アプリケーションがandroid 3にある場合.0以上のバージョンで実行すると、ビットマップはLruCacheから分離され、1つのビットマップに対する弱い参照がHashSetに配置され、inBitmapでの使用に備えられます.
HashSet<SoftReference<Bitmap>> mReusableBitmaps;
private LruCache<String, BitmapDrawable> mMemoryCache;

// If you're running on Honeycomb or newer, create
// a HashSet of references to reusable bitmaps.
if (Utils.hasHoneycomb()) {
    mReusableBitmaps = new HashSet<SoftReference<Bitmap>>();
}

mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {

    // Notify the removed entry that is no longer being cached.
    @Override
    protected void entryRemoved(boolean evicted, String key,
            BitmapDrawable oldValue, BitmapDrawable newValue) {
        if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
            // The removed entry is a recycling drawable, so notify it
            // that it has been removed from the memory cache.
            ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
        } else {
            // The removed entry is a standard BitmapDrawable.
            if (Utils.hasHoneycomb()) {
                // We're running on Honeycomb or later, so add the bitmap
                // to a SoftReference set for possible use with inBitmap later.
                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 we're running on Honeycomb or newer, try to use inBitmap.
    if (Utils.hasHoneycomb()) {
        addInBitmapOptions(options, cache);
    }
    ...
    return BitmapFactory.decodeFile(filename, options);
}
の下のコードフラグメントは、inBitmapの値として既存のビットマップを探すaddInBitmapOptions()メソッドを示している.この値は、inBitmapの値として使用されるのは、このような適切な一致値が存在する場合に限られることに注意してください(コードは、このような一致が存在すると仮定すべきではありません).
private static void addInBitmapOptions(BitmapFactory.Options options,
        ImageCache cache) {
    // inBitmap only works with mutable bitmaps, so force the decoder to
    // return mutable bitmaps.
    options.inMutable = true;

    if (cache != null) {
        // Try to find a bitmap to use for inBitmap.
        Bitmap inBitmap = cache.getBitmapFromReusableSet(options);

        if (inBitmap != null) {
            // If a suitable bitmap has been found, set it as the value of
            // inBitmap.
            options.inBitmap = inBitmap;
        }
    }
}

// This method iterates through the reusable bitmaps, looking for one 
// to use for 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()) {
                // Check to see it the item can be used for inBitmap.
                if (canUseForInBitmap(item, options)) {
                    bitmap = item;

                    // Remove from reusable set so it can't be used again.
                    iterator.remove();
                    break;
                }
            } else {
                // Remove from the set if the reference has been cleared.
                iterator.remove();
            }
        }
    }
    return bitmap;
}
最後に、この方法は、inBitmapのための寸法条件を満たす候補ビットマップを決定する.
static boolean canUseForInBitmap(
        Bitmap candidate, BitmapFactory.Options targetOptions) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        // From Android 4.4 (KitKat) onward we can re-use if the byte size of
        // the new bitmap is smaller than the reusable bitmap candidate
        // allocation byte count.
        int width = targetOptions.outWidth / targetOptions.inSampleSize;
        int height = targetOptions.outHeight / targetOptions.inSampleSize;
        int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
        return byteCount <= candidate.getAllocationByteCount();
    }

    // On earlier versions, the dimensions must match exactly and the inSampleSize must be 1
    return candidate.getWidth() == targetOptions.outWidth
            && candidate.getHeight() == targetOptions.outHeight
            && targetOptions.inSampleSize == 1;
}

/**
 * A helper function to return the byte usage per pixel of a bitmap based on its configuration.
 */
static int getBytesPerPixel(Config config) {
    if (config == Config.ARGB_8888) {
        return 4;
    } else if (config == Config.RGB_565) {
        return 2;
    } else if (config == Config.ARGB_4444) {
        return 2;
    } else if (config == Config.ALPHA_8) {
        return 1;
    }
    return 1;
}