बिटमैप को कैश मेमोरी में सेव करना

ध्यान दें: ज़्यादातर मामलों के लिए, हमारा सुझाव है कि कि आप Glide का इस्तेमाल करते हैं लाइब्रेरी का इस्तेमाल किया जा सकता है, ताकि ऐप्लिकेशन में बिटमैप फ़ेच किए जा सकें, डिकोड किए जा सकें, और दिखाए जा सकें. ग्लाइड करके ऐब्सट्रैक्ट ज़्यादातर, इन्हें मैनेज करने में मुश्किल आ रही है और Android पर बिटमैप और अन्य इमेज पर काम करने से जुड़े अन्य टास्क. Glide का इस्तेमाल करने और उसे डाउनलोड करने के बारे में जानकारी के लिए, यहां जाएं GitHub पर Glide डेटा स्टोर करने की जगह.

अपने यूज़र इंटरफ़ेस (यूआई) में एक बिट मैप लोड करना आसान होता है, लेकिन अब चीज़ें बहुत बड़ी हो जाती हैं अगर आपको एक ही बार में इमेज का ज़्यादा बड़ा सेट लोड करना हो, तो यह काफ़ी मुश्किल काम है. कई मामलों में (जैसे, ListView, GridView या ViewPager जैसे कॉम्पोनेंट), स्क्रीन पर मौजूद इमेज की कुल संख्या जितना जल्दी हो सके, स्क्रीन पर अनलिमिटेड स्क्रोल करने की सुविधा मिलती है.

मेमोरी के इस्तेमाल को इस तरह के कॉम्पोनेंट के साथ कम करके रखा जाता है. इसके लिए, व्यू के दौरान बच्चे के व्यू को रीसाइकल किया जाता है करते हैं. यह मानते हुए कि गार्बिज कलेक्टर आपके लोड किए गए बिटमैप को भी खाली कर देता है, यह मानते हुए कि आपको का इस्तेमाल लंबे समय तक रहा. यह पूरी तरह से अच्छा है, लेकिन एक आसान और तेज़ी से लोड होने वाले यूज़र इंटरफ़ेस (यूआई) को बनाए रखने के लिए आप चाहते हैं कि जब भी इमेज दोबारा स्क्रीन पर आएं, तो उन्हें लगातार प्रोसेस न किया जाए. यादगार लम्हा और डिस्क कैश मेमोरी अक्सर मददगार साबित हो सकती है. इससे कॉम्पोनेंट प्रोसेस की गई इमेज को तुरंत फिर से लोड कर पाते हैं.

इस लेसन में आपको रिस्पॉन्सिवनेस को बेहतर बनाने के लिए मेमोरी और डिस्क बिटमैप कैश का इस्तेमाल करने का तरीका बताया गया है और कई बिटमैप लोड करते समय आपके यूज़र इंटरफ़ेस (यूआई) की तरलता.

मेमोरी कैश का इस्तेमाल करें

कैश मेमोरी की मदद से, बिटमैप को तेज़ी से ऐक्सेस किया जा सकता है. इसके लिए, ज़रूरी ऐप्लिकेशन इस्तेमाल करना पड़ता है मेमोरी. LruCache क्लास (वापस इस्तेमाल करने के लिए सहायता लाइब्रेरी में भी उपलब्ध है एपीआई लेवल 4 तक सीमित है) किसी मज़बूत रेफ़र किए गए LinkedHashMap में मौजूद ऑब्जेक्ट और कम से कम कैश मेमोरी के तय साइज़ से ज़्यादा होने से पहले, हाल ही में इस्तेमाल किया गया सदस्य.

ध्यान दें: पहले, मेमोरी कैश को लागू करने का एक लोकप्रिय तरीका हालांकि, SoftReference या WeakReference बिट मैप कैश हम इसका सुझाव नहीं देते. Android 2.3 (एपीआई लेवल 9) और इसके बाद के वर्शन में, गै़रबेज कलेक्टर बेहतर होता है कम/कमज़ोर संदर्भ इकट्ठा करने के साथ ज़्यादा सक्रिय, जो उन्हें पूरी तरह से बेअसर बनाते हैं. इसके अलावा, Android 3.0 (एपीआई लेवल 11) से पहले के वर्शन में, बिटमैप का बैकिंग डेटा, नेटिव मेमोरी में सेव होता था. यह उसे अनुमानित तरीके से जारी नहीं किया जाता, जिससे ऐप्लिकेशन की रिलीज़ के लिए, मेमोरी की सीमाएं और क्रैश.

LruCache के लिए सही साइज़ चुनने के लिए, कई बातों पर ध्यान दिया जाता है पर ध्यान दिया जाना चाहिए, उदाहरण के लिए:

  • आपकी बाकी गतिविधि और/या ऐप्लिकेशन की मेमोरी कितनी ज़्यादा है?
  • स्क्रीन पर एक बार में कितनी इमेज दिखेंगी? कितने लोग आने के लिए तैयार रहें ऑन-स्क्रीन?
  • डिवाइस की स्क्रीन का साइज़ और सघनता क्या है? अतिरिक्त उच्च सघनता स्क्रीन (xhdpi) डिवाइस जैसे कि Galaxy Nexus को बड़ी कैश मेमोरी का इस्तेमाल करें, ताकि Nexus S (hdpi) जैसे डिवाइस की तुलना में, मेमोरी में उतनी ही संख्या में इमेज सेव हों.
  • बिट मैप कौन-कौनसे डाइमेंशन और कॉन्फ़िगरेशन हैं. इसलिए, हर एक बिट मैप में कितनी मेमोरी लेगा ऊपर?
  • इमेज कितनी बार ऐक्सेस की जाएंगी? क्या कुछ ऐप्लिकेशन को दूसरों के मुकाबले ज़्यादा तेज़ी से ऐक्सेस किया जा सकता है? अगर ऐसा है, तो हो सकता है कि आप कुछ आइटम को हमेशा मेमोरी में रखना चाहें या बिटमैप के अलग-अलग ग्रुप के लिए एक से ज़्यादा LruCache ऑब्जेक्ट रखना चाहें.
  • क्या आप वीडियो की संख्या और क्वालिटी के बीच संतुलन बना सकते हैं? कभी-कभी बड़ा स्टोर करना बेहतर होता है कम क्वालिटी वाले बिटमैप की संख्या, जिससे बेहतर क्वालिटी के वर्शन को दूसरे वर्शन में लोड किया जा सकता है बैकग्राउंड में चलने वाले टास्क.

सभी ऐप्लिकेशन के लिए कोई तय साइज़ या फ़ॉर्मूला नहीं है. इसलिए, अपने प्रॉडक्ट का विश्लेषण करने के लिए उसका इस्तेमाल करके उसका समाधान ढूंढ सकें. बहुत छोटा कैश होने की वजह से अतिरिक्त ओवरहेड होता है कोई फ़ायदा नहीं. बहुत बड़ी कैश मेमोरी की वजह से एक बार फिर से java.lang.OutOfMemory अपवाद हो सकते हैं और काम करने के लिए अपने ऐप्लिकेशन के शेष हिस्से को छोड़ दें.

बिटमैप के लिए LruCache सेट अप करने का उदाहरण यहां दिया गया है:

Kotlin

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

Java

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

ध्यान दें: इस उदाहरण में, ऐप्लिकेशन मेमोरी का आठवां हिस्सा कैश मेमोरी के लिए तय किया गया है. किसी सामान्य/एचडीपीआई डिवाइस पर इसका साइज़ कम से कम 4 एमबी (32/8) होना चाहिए. एक पूरा 800x480 रिज़ॉल्यूशन वाले डिवाइस में इमेज से भरी GridView स्क्रीन करीब 1.5 एमबी (800*480*4 बाइट) का इस्तेमाल करें. इससे कम से कम 2.5 पेजों की इमेज कैश मेमोरी में सेव होंगी मेमोरी.

किसी ImageView में बिटमैप लोड करते समय, LruCache पहले चेक किया जाता है. अगर कोई एंट्री मिलती है, तो ImageView को अपडेट करने के लिए तुरंत उसका इस्तेमाल किया जाता है. ऐसा न करने पर, इमेज को प्रोसेस करने के लिए एक बैकग्राउंड थ्रेड जनरेट किया जाता है:

Kotlin

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

Java

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 यह भी होना चाहिए कि को मेमोरी कैश में जोड़ने के लिए अपडेट किया गया:

Kotlin

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

Java

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 को लागू करने के तरीके का इस्तेमाल करता है. इसे Android सोर्स. यहां अपडेट किया गया उदाहरण कोड दिया गया है, जो मौजूदा मेमोरी कैश के साथ-साथ डिस्क कैश भी जोड़ता है:

Kotlin

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)
}

Java

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

ध्यान दें: डिस्क कैश मेमोरी को शुरू करने के लिए भी डिस्क से जुड़ी कार्रवाइयां ज़रूरी हैं इसलिए, यह मुख्य थ्रेड पर नहीं होना चाहिए. हालांकि, इसका मतलब यह है कि कैश मेमोरी को शुरू करने से पहले, उसे ऐक्सेस किया जा सकता है. इसे ठीक करने के लिए, ऊपर दिए गए तरीके में, एक लॉक ऑब्जेक्ट पक्का करता है कि ऐप्लिकेशन, डिस्क की कैश मेमोरी से तब तक नहीं पढ़ेगा, जब तक कि कैश मेमोरी में सेव नहीं हो जाता शुरू किया गया.

यूज़र इंटरफ़ेस (यूआई) थ्रेड में मेमोरी की कैश मेमोरी की जांच करने के दौरान, डिस्क की कैश मेमोरी की जांच बैकग्राउंड में की जाती है थ्रेड. यूज़र इंटरफ़ेस (यूआई) थ्रेड पर डिस्क की कार्रवाइयां कभी नहीं होनी चाहिए. जब इमेज प्रोसेसिंग यह होती है पूरा हो जाता है, तो अंतिम बिटमैप को भावी उपयोग के लिए मेमोरी और डिस्क कैश दोनों में जोड़ दिया जाता है.

कॉन्फ़िगरेशन में होने वाले बदलावों को मैनेज करना

रनटाइम कॉन्फ़िगरेशन में बदलाव, जैसे कि स्क्रीन ओरिएंटेशन में बदलाव की वजह से Android बंद हो जाता है और नए कॉन्फ़िगरेशन के साथ चल रही गतिविधि को फिर से चालू करें (इस व्यवहार के बारे में ज़्यादा जानकारी के लिए, रनटाइम में होने वाले बदलावों को मैनेज करना देखें). आपको अपनी सभी इमेज को फिर से प्रोसेस न करना पड़े, ताकि उपयोगकर्ता को आसानी से और तेज़ी से इमेज प्रोसेस करें कॉन्फ़िगरेशन में बदलाव होने पर अनुभव मिलेगा.

अच्छी बात यह है कि आपके पास बिटमैप का बढ़िया मेमोरी कैश होता है, जिसे आपने मेमोरी कैश का इस्तेमाल करें सेक्शन में बनाया है. इस कैश मेमोरी को नए Fragment का इस्तेमाल करके गतिविधि इंस्टेंस, जिसे setRetainInstance(true) को कॉल करके सुरक्षित किया गया है. गतिविधि के बाद फिर से बनाया गया, तो कायम रखे गए Fragment को फिर से अटैच कर दिया जाता है और आपको इसकी ऐक्सेस मिल जाती है मौजूदा कैश ऑब्जेक्ट है, जिससे इमेज को तुरंत फ़ेच किया जा सकता है और ImageView ऑब्जेक्ट में फिर से भरा जा सकता है.

यहां कॉन्फ़िगरेशन में LruCache ऑब्जेक्ट को बनाए रखने का उदाहरण दिया गया है Fragment का इस्तेमाल करके बदलाव करता है:

Kotlin

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

Java

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 के साथ और उसे बनाए बिना, दोनों बार घुमाकर देखें. आपको इमेज में थोड़ा या कोई अंतर नहीं दिखेगा, क्योंकि इमेज, गतिविधि को करीब-करीब पूरा कर देती हैं जब आप कैश मेमोरी बनाए रखते हैं तो मेमोरी से तुरंत मिटा दिया जाता है. मेमोरी कैश में नहीं मिली इमेज उम्मीद है कि यह डिस्क की कैश मेमोरी में उपलब्ध होगी. अगर ऐसा नहीं होता है, तो उसे सामान्य तरीके से प्रोसेस किया जाता है.