একটি মেমরী ক্যাশে মূল্যবান অ্যাপলিকেশন মেমরীর খরচের বিনিময়ে বিটম্যাপে দ্রুত প্রবেশগম্যতা প্রস্তাব করে। LruCache ক্লাস (এটা Support Library তেও পাওয়া যায়, এপিআই লেভেল ৪ এ ফিরে ব্যবহারের জন্য) ক্যাশিং বিটম্যাপের টাস্কের জন্য বিশেষ করে উপযোগী, একটি শক্তিশালী রেফারেন্স করা LinkedHashMap এর মধ্যে সম্প্রতি রেফারেন্স করা বস্তু রাখে এবং ক্যাশের নির্ধারিত সাইজ ছাড়িয়ে যাওয়ার পূর্বে কম সাম্প্রতিক ব্যবহৃত মেম্বার উচ্ছেদ করে।
নোট: অতীতে, একটি জনপ্রিয় মেমরী ক্যাশে বাস্তবায়ন ছিল SoftReference বা WeakReference বিটম্যাপ ক্যাশে, কিন্তু এটা সুপারিশ করা হয় না। অ্যান্ড্রয়েড ২.৩ (এপিআই লেভেল ৯) থেকে শুরু করে সফ্ট/উইক রেফারেন্স কালেকশন করা সহ গারবেজ কালেক্টর আরও আক্রমনাত্বক যা তাদেরকে কিছুটা অকার্যকর করে। উপরন্তু, অ্যান্ড্রয়েড ৩.০ (এপিআই লেভেল ১১) এর পুর্বে, একটি বিটম্যাপের ব্যাকিং ডাটা একটি স্থানীয় (নেটিভ) মেমরীতে স্টোর হতো যা অনুমেয় উপায়ে রিলিজ হয় না, সম্ভাব্য একটি অ্যাপলিকেশনকে একটি সংক্ষিপ্ত সময়ের জন্য এর মেমরী সীমাকে অতিক্রম করার এবং ধ্বংস করার কারন হয় ।
একটি LruCache এর জন্য উপযুক্ত সাইজ পছন্দ করার জন্য, কয়েকটি ফ্যাক্টর বিবেচনায় রাখতে হবে:
কতটুকু মেমরী ইনটেনসিভ হচ্ছে আপনার একটিভিটি এবং/বা অ্যাপলিকেশনের বাকী অংশ?
একসাথে কতগুলো ইমেজ অন-স্ক্রিন হতে পারে? কতগুলোকে অন-স্ক্রিনে প্রস্তুত হয়ে আসতে সহজপ্রাপ্য হওয়ার প্রয়োজন?
ডিভাইসের স্ক্রিনের সাইজ এবং ঘনত্ব কি? Galaxy Nexus এর মতো একটি এক্সট্রা হাই ডেনজিটি স্ক্রিনের (xhdpi) একটি মেমরী Nexus S (hdpi)এর মতো ডিভাইসে সাথে তুলনীয় তার মধ্যে একই সংখ্যক ইমেজ ধারন করতে একটি বড় আকারের ক্যাশে দরকার হবে ।
বিটম্যাপের ডাইমেনশন এবং কনফিগারেশন কি এবং তারপর প্রতিটা কি পরিমান মেমরী নিবে?
কত ঘণ ঘন এই ইমেজে প্রবেশ করা হতে পারে? কোনটাতে কি অন্যটার চেয়ে বেশী বার প্রবেশ করা হতে পারে? যদি হয়, সম্ভবত আপনি সবসময় কিছু আইটেম মেমরীতে রাখতে চাইতে পারেন বা বিটম্যাপের বিভিন্ন গ্রুপের জন্য এমনকি মাল্টিপল LruCache অবজেক্ট আছে।
আপনি কি পরিমানের বিপরীতে গুন এর ভারসাম্য রক্ষা করতে পারবেন? কিছু ক্ষেত্রে এটা কম গুনসম্পন্ন বিটম্যাপের একটি বড় সংখ্যাকে স্টোর করার জন্য আরও উপকারী হতে পারে, অন্য ব্যাকগ্রাউন্ড টাস্কের মধ্যে একটি উচ্চ গুণসম্পন্ন সংস্করণ সম্ভাব্য লোড করার জন্য।
এখানে কোন নির্দিস্ট সাইজ এবং ফর্মুলা নেই যা সকল অ্যাপলিকেশনের জন্য উপযোগী, এটা নির্ভর করে আপনার ব্যবহারের বিশ্লেষন এবং একটি সুন্দর সমাধানে আসার উপর। একটি ক্যাশে যা খুবই ছোট তা কোন সুবিধা ছাড়াই অতিরিক্ত ওভারহেড ঘটায়, একটি ক্যাশে যা বেশী বড় java.lang.OutOfMemory এক্সসেপশন ঘটায় এবং আপনার অ্যাপের বাকী ক্ষুদ্র মেমরী এর সাথে কাজ করার জন্য ছেড়ে দেয় ।
এখানে বিটম্যাপের জন্য একটি LruCache সেটআপের একটি উদাহরন আছে :
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
...
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
নোট: এই উদাহরনে, আটভাগের এক ভাগ অ্যাপলিকেশন মেমরী আপনার ক্যাশের জন্য বরাদ্দ। একটি নরমাল/ hdpi ডিভাইসে এটা ন্যুনতম ৪ MB এর কাছাকাছি (৩২/৮)। একটি ডিভাইসে 800x480 রেজ্যুলেশন সহকারে একটি ফুল স্ক্রিন GridView এ পূর্ণ করে থাকা ইমেজ প্রায় ১.৫ MB (800x480x4 bytes) ব্যবহার করতে পারে, সুতরাং এটা নিদেনপক্ষে ইমেজের প্রায় ২.৫ পেজ মেমরীতে জমিয়ে রাখে।
যখন একটি ImageView এর মধ্যে একটি বিটম্যাপ লোড করা হয়, LruCache প্রথমে চেক করা হয়। যদি একটি এন্ট্রি পাওয়া যায়, এটা তাৎক্ষনিকভাবে ImageView আপডেট করতে ব্যবহৃত হয়, অন্যথায় ইমেজ প্রসেস করতে একটি ব্যাকগ্রাউন্ড থ্রেড উৎপন্ন হয়:
public void loadBitmap(int resId, ImageView imageView) {
final String imageKey = String.valueOf(resId);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
mImageView.setImageBitmap(bitmap);
} else {
mImageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
task.execute(resId);
}
}
BitmapWorkerTask এর মেমরী ক্যাশেতে যুক্ত হতে আপডেট হওয়া প্রয়োজন:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100));
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
...
}