Android Application Development (using image cache)

Source: Internet
Author: User

Android developer:

It is easy to load an image on the UI. However, if you need to load multiple large images, it will become more complicated ,. In many cases (such as the components of the listview gridview or viewpager with an image), the total number of images on the screen increases suddenly with the scrolling on the screen and is basically infinite. To keep the memory usage in a stable range, these components will recycle the sub-view after it disappears in the screen, the garbage collection mechanism will release the memory space of the loaded images. Therefore, we recommend that you do not keep the images frequently referenced. This is a good practice. In order to ensure the smoothness and response speed of the page, you may not want to process and attach images frequently when the page returns. By caching memory and disk, you can quickly load Loaded Images.

Today I will share with you a piece of cache technology that can improve the smoothness and response speed of the UI and provide a good user experience.

How to cache data in the memory?

You can use the memory cache to quickly load cached images, but it will consume the application's memory space. The lrucache class (sdk4 can be supported through the compatible package) is suitable for image caching. It stores images by using linkedhashmap to ensure strong image reference, when the cache space exceeds the set limit, the early cache will be released.

Note: In the past, the commonly used memory cache was implemented through softreference or weakreference, but this is not recommended. The android2.3 (API level 9) spam collectors are more active in collecting soft/weak references, which makes them quite ineffective. In addition, before Android 3.0 (API level 11), the visible bitmap stored in native memory will not be released, which may cause the application to temporarily exceed its memory limit and crash.

 

To set an appropriate size for lrucache, consider the following factors:

  • What is the idle memory size of your application?

  • How many images do you want to display on the screen? How many images have you prepared for display?

  • What is the screen size and density of the device? Ultra-high-screen density devices (xhdpi) require a larger cache space when caching the same images than those of Nexus S (hdpi) devices.

  • The image size and attributes, and how much memory space does it occupy?

  • What is the Access Frequency of images? Is it more frequently used than other images? In this case, you may need to consider storing images in the memory for a long time or using different cache policies for different types of images.

  • How to balance the quality and quantity? In some cases, you may store frequently used low-quality images for User display, and then load high-quality images through asynchronous threads.

The image cache scheme does not have a fixed pattern to use all applications. You need to analyze the scheme based on the specific application scenarios of the application and select an appropriate scheme. The cache is too small to take advantage of the cache, too large may occupy too much memory, reduce application performance, or cause memory overflow exceptions,

The following is an example of using lrucache:

private LruCache mMemoryCache;                                                                          @Overrideprotected void onCreate(Bundle savedInstanceState) {     ...     // Get memory class of this device, exceeding this amount will throw an     // OutOfMemory exception.     final int memClass = ((ActivityManager) context.getSystemService(             Context.ACTIVITY_SERVICE)).getMemoryClass();                                                                              // Use 1/8th of the available memory for this memory cache.     final int cacheSize = 1024 * 1024 * memClass / 8;                                                                              mMemoryCache = new LruCache(cacheSize) {         @Override        protected int sizeOf(String key, Bitmap bitmap) {             // The cache size will be measured in bytes rather than number of items.             return bitmap.getByteCount();         }     };     ... }                                                                          public void addBitmapToMemoryCache(String key, Bitmap bitmap) {     if (getBitmapFromMemCache(key) == null) {         mMemoryCache.put(key, bitmap);     } }                                                                          public Bitmap getBitmapFromMemCache(String key) {     return mMemoryCache.get(key); }

Note: In this example, the memory allocated to the image cache by the Application of eight-member-one is about 4 MB (32/8) in the common/hdpi device ). 800 mb image space (480*2.5*4 bytes) is required for devices with X resolution in full screen mode of girdview, so that screen images can be cached in the memory.

 

When you use lrucache to add an image to imageview, first check whether the image exists. If you directly change the imageview, otherwise, the image will be loaded through the background thread:

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 needs to add the loaded image to the cache:

class BitmapWorkerTask extends AsyncTask {     ...     // 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;     }     ... }

 

How to use disk cache?

The memory cache is very efficient for accessing recently used images, but you cannot guarantee that it will always be in the cache. Components with large data volumes such as girdview are easily filled with memory cache. Your application may be interrupted by "incoming calls" and killed in the background, and the memory cache will become invalid. Once the user returns to the application, you need to re-process each image.

In this case, we can use the disk cache to store processed images. When images are no longer in memory, the reload time is reduced. Of course, loading images from disks is slower than loading images in memory, it needs to be done in the background thread because the disk read time is unknown.

Note: If you frequently access images, contentprovider should be a good place to store images, such as the gallery image management application.

The following is a simple implementation of disklrucache. However, for the recommended disklrucache solution, see the source code in android4.0 (libcore/luni/src/main/Java/libcore/IO/disklrucache. Java. This article uses the simple implementation in previous versions (another implementation in quick search ).

The following is an example of how to update disklrucache:

private DiskLruCache mDiskCache; private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB private static final String DISK_CACHE_SUBDIR = "thumbnails";                                    @Overrideprotected void onCreate(Bundle savedInstanceState) {     ...     // Initialize memory cache     ...     File cacheDir = getCacheDir(this, DISK_CACHE_SUBDIR);     mDiskCache = DiskLruCache.openCache(this, cacheDir, DISK_CACHE_SIZE);     ... }                                    class BitmapWorkerTask extends AsyncTask {     ...     // 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(String.valueOf(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     if (!mDiskCache.containsKey(key)) {         mDiskCache.put(key, bitmap);     } }                                    public Bitmap getBitmapFromDiskCache(String key) {     return mDiskCache.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. public static File getCacheDir(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.getExternalStorageState() == Environment.MEDIA_MOUNTED             || !Environment.isExternalStorageRemovable() ?                     context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();                                        return new File(cachePath + File.separator + uniqueName); }

The memory cache check is performed in the UI thread, and the disk cache check is performed in the background thread. Hard Disk operations should not be performed in the UI thread. After the image processing is complete, add it to the memory and disk cache in use.

How can I handle configuration changes?

When the configuration changes during application running, for example, when the screen direction changes, for the purpose of applying the new configuration, Android will destroy and re-run the current activity. At this time, you may not want to reload images for a quick and smooth user experience.

Thanks to the cache technology, the cache can be passed to the new activity through setretaininstance (true). After the activity is restarted, you can re-use the existing cache through the attached fragment, in this way, the image view can be quickly loaded.

The following is an example of reusing the existing cache with fragment when the configuration changes:

private LruCache mMemoryCache;                   @Overrideprotected void onCreate(Bundle savedInstanceState) {     ...     RetainFragment mRetainFragment =             RetainFragment.findOrCreateRetainFragment(getFragmentManager());     mMemoryCache = RetainFragment.mRetainedCache;     if (mMemoryCache == null) {         mMemoryCache = new LruCache(cacheSize) {             ... // Initialize cache here as usual         }         mRetainFragment.mRetainedCache = mMemoryCache;     }     ... }                   class RetainFragment extends Fragment {     private static final String TAG = "RetainFragment";     public LruCache mRetainedCache;                       public RetainFragment() {}                       public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {         RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);         if (fragment == null) {             fragment = new RetainFragment();         }         return fragment;     }                       @Override    public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setRetainInstance(true);     } }

To test whether the screen is forcibly rotated when fragment is used or not used, you will find that there is almost no lag when loading images from the reserved memory cache.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.