ক্যাশিং বিটম্যাপ

দ্রষ্টব্য: বেশিরভাগ ক্ষেত্রে, আমরা সুপারিশ করি যে আপনি আপনার অ্যাপে বিটম্যাপ আনতে, ডিকোড করতে এবং প্রদর্শন করতে গ্লাইড লাইব্রেরি ব্যবহার করুন। অ্যান্ড্রয়েডে বিটম্যাপ এবং অন্যান্য চিত্রগুলির সাথে কাজ করার সাথে সম্পর্কিত এইগুলি এবং অন্যান্য কাজগুলি পরিচালনা করার বেশিরভাগ জটিলতা গ্লাইড বিমূর্ত করে। গ্লাইড ব্যবহার এবং ডাউনলোড করার বিষয়ে তথ্যের জন্য, GitHub-এ গ্লাইড সংগ্রহস্থলে যান।

আপনার ইউজার ইন্টারফেসে (UI) একটি একক বিটম্যাপ লোড করা সহজ, তবে আপনি যদি একবারে একটি বড় সেট চিত্র লোড করতে চান তবে জিনিসগুলি আরও জটিল হয়ে যায়। অনেক ক্ষেত্রে (যেমন ListView , GridView বা ViewPager এর মতো উপাদানগুলির সাথে), শীঘ্রই স্ক্রীনে স্ক্রোল করা হতে পারে এমন চিত্রগুলির সাথে মিলিত অন-স্ক্রীন ছবির মোট সংখ্যা মূলত সীমাহীন।

স্ক্রীনের বাইরে চলে যাওয়ার সাথে সাথে শিশুর দৃষ্টিভঙ্গি পুনর্ব্যবহার করে মেমরির ব্যবহার কম রাখা হয়। আবর্জনা সংগ্রাহক আপনার লোড করা বিটম্যাপগুলিকেও মুক্ত করে, ধরে নিচ্ছেন যে আপনি দীর্ঘজীবী রেফারেন্স রাখেন না। এটি সবই ভাল এবং ভাল, কিন্তু একটি তরল এবং দ্রুত-লোডিং UI রাখার জন্য আপনি প্রতিবার পর্দায় ফিরে আসার সময় এই ছবিগুলিকে ক্রমাগত প্রক্রিয়াকরণ এড়াতে চান৷ একটি মেমরি এবং ডিস্ক ক্যাশে প্রায়শই এখানে সাহায্য করতে পারে, উপাদানগুলিকে প্রক্রিয়াকৃত চিত্রগুলি দ্রুত পুনরায় লোড করার অনুমতি দেয়।

একাধিক বিটম্যাপ লোড করার সময় আপনার UI এর প্রতিক্রিয়াশীলতা এবং তরলতা উন্নত করতে এই পাঠটি আপনাকে একটি মেমরি এবং ডিস্ক বিটম্যাপ ক্যাশে ব্যবহার করে নিয়ে যায়।

একটি মেমরি ক্যাশে ব্যবহার করুন

একটি মেমরি ক্যাশে মূল্যবান অ্যাপ্লিকেশন মেমরি গ্রহণের খরচে বিটম্যাপে দ্রুত অ্যাক্সেস সরবরাহ করে। LruCache ক্লাস (এছাড়াও API লেভেল 4-এ ব্যবহারের জন্য সমর্থন লাইব্রেরিতে উপলব্ধ) বিটম্যাপ ক্যাশ করার জন্য বিশেষভাবে উপযুক্ত, সাম্প্রতিক রেফারেন্স করা বস্তুগুলিকে একটি শক্তিশালী রেফারেন্সযুক্ত LinkedHashMap এ রাখা এবং ক্যাশে অতিক্রম করার আগে সর্বনিম্ন ব্যবহার করা সদস্যকে উচ্ছেদ করা। নির্ধারিত আকার।

দ্রষ্টব্য: অতীতে, একটি জনপ্রিয় মেমরি ক্যাশে বাস্তবায়ন একটি SoftReference বা WeakReference বিটম্যাপ ক্যাশে ছিল, তবে এটি সুপারিশ করা হয় না। অ্যান্ড্রয়েড 2.3 (এপিআই লেভেল 9) থেকে শুরু করে আবর্জনা সংগ্রাহক নরম/দুর্বল রেফারেন্স সংগ্রহের ক্ষেত্রে আরও আক্রমণাত্মক যা তাদের মোটামুটি অকার্যকর করে তোলে। উপরন্তু, অ্যান্ড্রয়েড 3.0 (এপিআই লেভেল 11) এর আগে, একটি বিটম্যাপের ব্যাকিং ডেটা নেটিভ মেমরিতে সংরক্ষিত ছিল যা একটি অনুমানযোগ্য পদ্ধতিতে প্রকাশ করা হয় না, সম্ভাব্যভাবে একটি অ্যাপ্লিকেশন সংক্ষিপ্তভাবে মেমরির সীমা অতিক্রম করে এবং ক্র্যাশ করে।

একটি LruCache এর জন্য একটি উপযুক্ত আকার চয়ন করার জন্য, অনেকগুলি কারণ বিবেচনা করা উচিত, উদাহরণস্বরূপ:

  • আপনার বাকি কার্যকলাপ এবং/অথবা অ্যাপ্লিকেশন কতটা মেমরি নিবিড়?
  • একবারে কতগুলো ছবি অন-স্ক্রিন হবে? পর্দায় আসার জন্য কতজনকে প্রস্তুত থাকতে হবে?
  • ডিভাইসটির পর্দার আকার এবং ঘনত্ব কত? গ্যালাক্সি নেক্সাসের মতো একটি অতিরিক্ত উচ্চ ঘনত্বের স্ক্রিন (xhdpi) ডিভাইস নেক্সাস এস (এইচডিপিআই) এর মতো একটি ডিভাইসের তুলনায় মেমরিতে একই সংখ্যক ছবি ধরে রাখতে একটি বড় ক্যাশের প্রয়োজন হবে।
  • বিটম্যাপগুলি কি মাত্রা এবং কনফিগারেশন এবং তাই প্রতিটি কত মেমরি গ্রহণ করবে?
  • কত ঘন ঘন ছবি অ্যাক্সেস করা হবে? কিছু অন্যদের তুলনায় আরো ঘন ঘন অ্যাক্সেস করা হবে? যদি তাই হয়, সম্ভবত আপনি কিছু আইটেম সবসময় মেমরিতে রাখতে চাইতে পারেন বা বিটম্যাপের বিভিন্ন গ্রুপের জন্য একাধিক LruCache অবজেক্ট থাকতে পারেন।
  • আপনি কি পরিমাণের বিপরীতে গুণমানের ভারসাম্য রাখতে পারেন? কখনও কখনও এটি একটি বড় সংখ্যক নিম্ন মানের বিটম্যাপ সংরক্ষণ করা আরও কার্যকর হতে পারে, সম্ভাব্যভাবে অন্য ব্যাকগ্রাউন্ড টাস্কে একটি উচ্চ মানের সংস্করণ লোড করা।

কোনো নির্দিষ্ট আকার বা সূত্র নেই যা সমস্ত অ্যাপ্লিকেশনের জন্য উপযুক্ত, এটি আপনার উপর নির্ভর করে আপনার ব্যবহার বিশ্লেষণ করা এবং একটি উপযুক্ত সমাধান নিয়ে আসা। খুব ছোট একটি ক্যাশে অতিরিক্ত ওভারহেডের কোনো সুবিধা ছাড়াই কারণ, একটি খুব বড় ক্যাশে আবার java.lang.OutOfMemory ব্যতিক্রম ঘটাতে পারে এবং আপনার অ্যাপের বাকি মেমরিটি কাজ করার জন্য রেখে দিতে পারে।

বিটম্যাপের জন্য একটি LruCache সেট আপ করার একটি উদাহরণ এখানে:

কোটলিন

private lateinit var memoryCache: LruCache<String, Bitmap>

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    // Get max available VM memory, exceeding this amount will throw an
    // OutOfMemory exception. Stored in kilobytes as LruCache takes an
    // int in its constructor.
    val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()

    // Use 1/8th of the available memory for this memory cache.
    val cacheSize = maxMemory / 8

    memoryCache = object : LruCache<String, Bitmap>(cacheSize) {

        override fun sizeOf(key: String, bitmap: Bitmap): Int {
            // The cache size will be measured in kilobytes rather than
            // number of items.
            return bitmap.byteCount / 1024
        }
    }
    ...
}

জাভা

private LruCache<String, Bitmap> memoryCache;

@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;

    memoryCache = 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) {
        memoryCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromMemCache(String key) {
    return memoryCache.get(key);
}

দ্রষ্টব্য: এই উদাহরণে, অ্যাপ্লিকেশন মেমরির এক অষ্টমাংশ আমাদের ক্যাশের জন্য বরাদ্দ করা হয়েছে। একটি সাধারণ/এইচডিপিআই ডিভাইসে এটি সর্বনিম্ন প্রায় 4MB (32/8)। 800x480 রেজোলিউশনের একটি ডিভাইসে ইমেজ দিয়ে ভরা একটি পূর্ণ স্ক্রীন GridView প্রায় 1.5MB (800*480*4 বাইট) ব্যবহার করবে, তাই এটি মেমরিতে ন্যূনতম 2.5 পৃষ্ঠার ছবি ক্যাশে করবে।

একটি ImageView এ একটি বিটম্যাপ লোড করার সময়, LruCache প্রথমে চেক করা হয়। যদি একটি এন্ট্রি পাওয়া যায়, এটি অবিলম্বে ImageView আপডেট করতে ব্যবহার করা হয়, অন্যথায় চিত্রটি প্রক্রিয়া করার জন্য একটি পটভূমি থ্রেড তৈরি করা হয়:

কোটলিন

fun loadBitmap(resId: Int, imageView: ImageView) {
    val imageKey: String = resId.toString()

    val bitmap: Bitmap? = getBitmapFromMemCache(imageKey)?.also {
        mImageView.setImageBitmap(it)
    } ?: run {
        mImageView.setImageResource(R.drawable.image_placeholder)
        val task = BitmapWorkerTask()
        task.execute(resId)
        null
    }
}

জাভা

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 ও আপডেট করা দরকার:

কোটলিন

private inner class BitmapWorkerTask : AsyncTask<Int, Unit, Bitmap>() {
    ...
    // Decode image in background.
    override fun doInBackground(vararg params: Int?): Bitmap? {
        return params[0]?.let { imageId ->
            decodeSampledBitmapFromResource(resources, imageId, 100, 100)?.also { bitmap ->
                addBitmapToMemoryCache(imageId.toString(), bitmap)
            }
        }
    }
    ...
}

জাভা

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;
    }
    ...
}

একটি ডিস্ক ক্যাশে ব্যবহার করুন

একটি মেমরি ক্যাশে সম্প্রতি দেখা বিটম্যাপগুলিতে অ্যাক্সেসের গতি বাড়ানোর জন্য দরকারী, তবে আপনি এই ক্যাশে উপলব্ধ চিত্রগুলির উপর নির্ভর করতে পারবেন না। বৃহত্তর ডেটাসেট সহ GridView এর মতো উপাদানগুলি সহজেই একটি মেমরি ক্যাশে পূরণ করতে পারে। আপনার অ্যাপ্লিকেশনটি একটি ফোন কলের মতো অন্য কাজ দ্বারা বাধাগ্রস্ত হতে পারে এবং পটভূমিতে থাকাকালীন এটি মেরে যেতে পারে এবং মেমরি ক্যাশে ধ্বংস হয়ে যেতে পারে। একবার ব্যবহারকারী পুনরায় শুরু করলে, আপনার অ্যাপ্লিকেশনটিকে আবার প্রতিটি চিত্র প্রক্রিয়া করতে হবে।

এই ক্ষেত্রে একটি ডিস্ক ক্যাশে ব্যবহার করা যেতে পারে প্রক্রিয়াকৃত বিটম্যাপগুলিকে অব্যাহত রাখতে এবং লোড হওয়ার সময় কমাতে সাহায্য করে যেখানে চিত্রগুলি আর মেমরি ক্যাশে পাওয়া যায় না। অবশ্যই, ডিস্ক থেকে ছবি আনা মেমরি থেকে লোড করার চেয়ে ধীর এবং এটি একটি ব্যাকগ্রাউন্ড থ্রেডে করা উচিত, কারণ ডিস্ক পড়ার সময় অনির্দেশ্য হতে পারে।

দ্রষ্টব্য: একটি ContentProvider ক্যাশে করা ছবিগুলিকে সঞ্চয় করার জন্য আরও উপযুক্ত জায়গা হতে পারে যদি সেগুলি আরও ঘন ঘন অ্যাক্সেস করা হয়, উদাহরণস্বরূপ একটি চিত্র গ্যালারী অ্যাপ্লিকেশনে৷

এই ক্লাসের নমুনা কোডটি একটি DiskLruCache বাস্তবায়ন ব্যবহার করে যা অ্যান্ড্রয়েড উৎস থেকে নেওয়া হয়। এখানে আপডেট করা উদাহরণ কোড যা বিদ্যমান মেমরি ক্যাশে ছাড়াও একটি ডিস্ক ক্যাশে যুক্ত করে:

কোটলিন

private const val DISK_CACHE_SIZE = 1024 * 1024 * 10 // 10MB
private const val DISK_CACHE_SUBDIR = "thumbnails"
...
private var diskLruCache: DiskLruCache? = null
private val diskCacheLock = ReentrantLock()
private val diskCacheLockCondition: Condition = diskCacheLock.newCondition()
private var diskCacheStarting = true

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    // Initialize memory cache
    ...
    // Initialize disk cache on background thread
    val cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR)
    InitDiskCacheTask().execute(cacheDir)
    ...
}

internal inner class InitDiskCacheTask : AsyncTask<File, Void, Void>() {
    override fun doInBackground(vararg params: File): Void? {
        diskCacheLock.withLock {
            val cacheDir = params[0]
            diskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE)
            diskCacheStarting = false // Finished initialization
            diskCacheLockCondition.signalAll() // Wake any waiting threads
        }
        return null
    }
}

internal inner class  BitmapWorkerTask : AsyncTask<Int, Unit, Bitmap>() {
    ...

    // Decode image in background.
    override fun doInBackground(vararg params: Int?): Bitmap? {
        val imageKey = params[0].toString()

        // Check disk cache in background thread
        return getBitmapFromDiskCache(imageKey) ?:
                // Not found in disk cache
                decodeSampledBitmapFromResource(resources, params[0], 100, 100)
                        ?.also {
                            // Add final bitmap to caches
                            addBitmapToCache(imageKey, it)
                        }
    }
}

fun addBitmapToCache(key: String, bitmap: Bitmap) {
    // Add to memory cache as before
    if (getBitmapFromMemCache(key) == null) {
        memoryCache.put(key, bitmap)
    }

    // Also add to disk cache
    synchronized(diskCacheLock) {
        diskLruCache?.apply {
            if (!containsKey(key)) {
                put(key, bitmap)
            }
        }
    }
}

fun getBitmapFromDiskCache(key: String): Bitmap? =
        diskCacheLock.withLock {
            // Wait while disk cache is started from background thread
            while (diskCacheStarting) {
                try {
                    diskCacheLockCondition.await()
                } catch (e: InterruptedException) {
                }

            }
            return diskLruCache?.get(key)
        }

// Creates a unique subdirectory of the designated app cache directory. Tries to use external
// but if not mounted, falls back on internal storage.
fun getDiskCacheDir(context: Context, uniqueName: String): File {
    // Check if media is mounted or storage is built-in, if so, try and use external cache dir
    // otherwise use internal cache dir
    val cachePath =
            if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState()
                    || !isExternalStorageRemovable()) {
                context.externalCacheDir.path
            } else {
                context.cacheDir.path
            }

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

জাভা

private DiskLruCache diskLruCache;
private final Object diskCacheLock = new Object();
private boolean diskCacheStarting = 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 (diskCacheLock) {
            File cacheDir = params[0];
            diskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
            diskCacheStarting = false; // Finished initialization
            diskCacheLock.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) {
        memoryCache.put(key, bitmap);
    }

    // Also add to disk cache
    synchronized (diskCacheLock) {
        if (diskLruCache != null && diskLruCache.get(key) == null) {
            diskLruCache.put(key, bitmap);
        }
    }
}

public Bitmap getBitmapFromDiskCache(String key) {
    synchronized (diskCacheLock) {
        // Wait while disk cache is started from background thread
        while (diskCacheStarting) {
            try {
                diskCacheLock.wait();
            } catch (InterruptedException e) {}
        }
        if (diskLruCache != null) {
            return diskLruCache.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);
}

দ্রষ্টব্য: এমনকি ডিস্ক ক্যাশে আরম্ভ করার জন্য ডিস্ক অপারেশন প্রয়োজন এবং তাই মূল থ্রেডে স্থান নেওয়া উচিত নয়। যাইহোক, এর অর্থ এই যে শুরু করার আগে ক্যাশে অ্যাক্সেস করার সুযোগ রয়েছে। এটি মোকাবেলা করার জন্য, উপরের বাস্তবায়নে, একটি লক অবজেক্ট নিশ্চিত করে যে ক্যাশে আরম্ভ না হওয়া পর্যন্ত অ্যাপটি ডিস্ক ক্যাশে থেকে পড়বে না।

UI থ্রেডে মেমরি ক্যাশে চেক করা হলে, ব্যাকগ্রাউন্ড থ্রেডে ডিস্ক ক্যাশে চেক করা হয়। ডিস্ক অপারেশন কখনই UI থ্রেডে হওয়া উচিত নয়। ইমেজ প্রসেসিং সম্পূর্ণ হলে, ভবিষ্যতে ব্যবহারের জন্য মেমরি এবং ডিস্ক ক্যাশে উভয়েই চূড়ান্ত বিটম্যাপ যোগ করা হয়।

কনফিগারেশন পরিবর্তন পরিচালনা করুন

রানটাইম কনফিগারেশন পরিবর্তন, যেমন একটি স্ক্রীন অভিযোজন পরিবর্তন, নতুন কনফিগারেশনের সাথে চলমান কার্যকলাপকে ধ্বংস এবং পুনরায় চালু করার কারণ (এই আচরণ সম্পর্কে আরও তথ্যের জন্য, রানটাইম পরিবর্তন পরিচালনা করা দেখুন)। কনফিগারেশন পরিবর্তনের সময় ব্যবহারকারীর একটি মসৃণ এবং দ্রুত অভিজ্ঞতা থাকে তাই আপনি আপনার সমস্ত ছবি আবার প্রক্রিয়া করা এড়াতে চান।

ভাগ্যক্রমে, আপনার কাছে বিটম্যাপের একটি চমৎকার মেমরি ক্যাশে রয়েছে যা আপনি একটি মেমরি ক্যাশে ব্যবহার করুন বিভাগে তৈরি করেছেন। এই ক্যাশে একটি Fragment ব্যবহার করে নতুন অ্যাক্টিভিটি ইন্সট্যান্সে পাস করা যেতে পারে যা setRetainInstance(true) কল করে সংরক্ষণ করা হয়। ক্রিয়াকলাপটি পুনরায় তৈরি করার পরে, এই ধরে রাখা Fragment পুনরায় সংযুক্ত করা হয় এবং আপনি বিদ্যমান ক্যাশে অবজেক্টে অ্যাক্সেস লাভ করেন, যাতে ছবিগুলিকে দ্রুত আনা যায় এবং ImageView অবজেক্টগুলিতে পুনরায় জনবহুল করা যায়।

এখানে একটি Fragment ব্যবহার করে কনফিগারেশন পরিবর্তন জুড়ে একটি LruCache অবজেক্ট ধরে রাখার একটি উদাহরণ রয়েছে:

কোটলিন

private const val TAG = "RetainFragment"
...
private lateinit var mMemoryCache: LruCache<String, Bitmap>

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    val retainFragment = RetainFragment.findOrCreateRetainFragment(supportFragmentManager)
    mMemoryCache = retainFragment.retainedCache ?: run {
        LruCache<String, Bitmap>(cacheSize).also { memoryCache ->
            ... // Initialize cache here as usual
            retainFragment.retainedCache = memoryCache
        }
    }
    ...
}

class RetainFragment : Fragment() {
    var retainedCache: LruCache<String, Bitmap>? = null

    companion object {
        fun findOrCreateRetainFragment(fm: FragmentManager): RetainFragment {
            return (fm.findFragmentByTag(TAG) as? RetainFragment) ?: run {
                RetainFragment().also {
                    fm.beginTransaction().add(it, TAG).commit()
                }
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        retainInstance = true
    }
}

জাভা

private LruCache<String, Bitmap> memoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    RetainFragment retainFragment =
            RetainFragment.findOrCreateRetainFragment(getFragmentManager());
    memoryCache = retainFragment.retainedCache;
    if (memoryCache == null) {
        memoryCache = new LruCache<String, Bitmap>(cacheSize) {
            ... // Initialize cache here as usual
        }
        retainFragment.retainedCache = memoryCache;
    }
    ...
}

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

    public RetainFragment() {}

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

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }
}

এটি পরীক্ষা করার জন্য, Fragment ধরে না রেখে একটি ডিভাইস ঘোরানোর চেষ্টা করুন। আপনি যখন ক্যাশে ধরে রাখেন তখন ইমেজগুলি প্রায় সাথে সাথে মেমরি থেকে কার্যকলাপটি পূরণ করে বলে আপনার কোন ব্যবধান নেই তা লক্ষ্য করা উচিত। মেমরি ক্যাশে পাওয়া যায় না এমন কোনো ছবি আশা করা যায় ডিস্ক ক্যাশে পাওয়া যায়, যদি না হয়, সেগুলি যথারীতি প্রক্রিয়া করা হয়।