একটি মেমরী ক্যাশে সাম্প্রতিক ভিউ হওয়া বিটম্যাপে একটি গতিশীল প্রবেশের জন্য উপকারী, কিন্তু আপনি এই ক্যশেতে ইমেজের সহজপ্রাপ্য হওয়ার উপর নির্ভর করতে পারেন না। বড় আকারের ডাটাসেট সহকারে GridView এর মতো কম্পোনেন্ট সহজেই একটি মেমরী ক্যাশে পূর্ণ করতে পারে। আপনার অ্যাপলিকেশন অন্য কোন টাস্ক যেমন একটি ফোন কল দ্বারা বাধাগ্রস্থ হতে পারে, এবং যখন ব্যাকগ্রান্ডের মধ্যে এটা হতে পারে এবং মেমরী ক্যাশে ধ্বংস হবে। যখনই ইউজার আবার শুরু করে, আপনার অ্যাপলিকেশনকে প্রতিটা ইমেজ আবার শুরু করতে হয়।
একটি ডিস্ক ক্যাশে এই ক্ষেত্রে প্রসেস করা বিটম্যাপ অটলভাবে চালিয়ে যাওয়ার কাজে ব্যবহার করা যেতে পারে এবং লোড করার সময় কমাতে পারে যেখানে ইমেজ মেমরী ক্যাশের মধ্যে আর পাওয়া যাবে না। অবশ্যই, ডিস্ক থেকে ইমেজ আনয়ন করা মেমরী থেকে লোড করার চেয়ে ধীরগতির এবং অবশ্যই ব্যাকগ্রাউন্ড থ্রেডে সম্পন্ন করতে হবে, যেহেতু ডিস্ক রিড সব সময় অঅনুমেয়।
নোট: ক্যাশে ইমেজ স্টোর করতে ContentProvider একটা ভালো জায়গা হতে পারে, যদি তাতে বারবার প্রবেশ করা হয়, উদাহরণস্বরূপ, একটি ইমেজ গ্যালারী অ্যাপলিকেশনের মধ্যে।
এই ক্লাসের নমুনা কোড একটি DiskLruCache বাস্তবায়ন ব্যবহার করে যাকে Android source (https://android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/libcore/io/DiskLruCache.java) থেকে টানা হয়। এখানে একটি আপডেট উদাহরণ কোড আছে যা বিদ্যমান মেমরী ক্যাশে অতিরিক্ত হিসাবে একটি ডিস্ক ক্যাশে যুক্ত করে:
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) {
...
// Initialize memory cache
...
// Initialize disk cache on background thread
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; // Finished initialization
mDiskCacheLock.notifyAll(); // Wake any waiting threads
}
return null;
}
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
final String imageKey = String.valueOf(params[0]);
// Check disk cache in background thread
Bitmap bitmap = getBitmapFromDiskCache(imageKey);
if (bitmap == null) { // Not found in disk cache
// Process as normal
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100));
}
// Add final bitmap to caches
addBitmapToCache(imageKey, bitmap);
return bitmap;
}
...
}
public void addBitmapToCache(String key, Bitmap bitmap) {
// Add to memory cache as before
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
// Also add to disk cache
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
mDiskLruCache.put(key, bitmap);
}
}
}
public Bitmap getBitmapFromDiskCache(String key) {
synchronized (mDiskCacheLock) {
// Wait while disk cache is started from background thread
while (mDiskCacheStarting) {
try {
mDiskCacheLock.wait();
} catch (InterruptedException e) {}
}
if (mDiskLruCache != null) {
return mDiskLruCache.get(key);
}
}
return null;
}
// Creates a unique subdirectory of the designated app cache directory. Tries to use external
// but if not mounted, falls back on internal storage.
public static File getDiskCacheDir(Context context, String uniqueName) {
// Check if media is mounted or storage is built-in, if so, try and use external cache dir
// otherwise use internal cache dir
final String cachePath =
Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
!isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
context.getCacheDir().getPath();
return new File(cachePath + File.separator + uniqueName);
}
নোট: ডিস্ক ক্যাশে আরম্ভ করা ডিস্ক অপারেশন আশা করতে পারে এবং সেজন্য মেইন থ্রেডে ঘটা উচিত না। কিন্তু, এটা বোঝায় সেখানে একটি সুযোগ আছে ক্যাশে আরম্ভ হওয়ার পূর্বেই সেখানে প্রবেশ করা হয়। এটা সনাক্ত করতে, উপরোক্ত বাস্তবায়নে,একটি লক করা অবজেক্ট নিশ্চিত করে যে অ্যাপ ডিস্ক ক্যাশে থেকে পড়ে না যতক্ষন না ক্যাশে আরম্ভ করা হয়।
যখন মেমরী ক্যাশে ইউআই থ্রেডের মধ্যে চেক করা হয়, ডিস্ক ক্যাশে ব্যাকগ্রাউন্ড থ্রেডে চেক করা হয়। ডিস্ক অপারেশন ইউআই থ্রেডে সংঘটিত হওয়া উচিন নয়। যখন ইমেজ প্রসেস করা সম্পূর্ণ হয়, তখন চুড়ান্ত বিটম্যাপ মেমরী এবং ডিস্ক ক্যাশে দুইটাতেই ভবিষ্যত ব্যবহারের জন্য যুক্ত হয়।