Bitmap cache mechanism, bitmap cache

Source: Internet
Author: User

Bitmap cache mechanism, bitmap cache
The Bitmap cache mechanism loads a bitmap to the UI, which is relatively simple and straightforward. However, if we load a large amount of bitmap data at a time, it becomes complicated. In many cases (for example, ListVIew, GridView, or ViewPager), the displayed image and the image to be rolled are generally unlimited. Memory usage can be reduced by reclaiming components that are removed from the screen. Android's garbage collection mechanism also releases your resources if they are not referenced. This mechanism is good, but in order to get a smooth and fast UI experience, we want to avoid repeated image downloads. A local memory cache method provides great help to quickly reload locally cached resources. This chapter describes how to use the memory cache mechanism to improve the UI response speed and smoothness. When loading multiple images. Original article: Workshop. The LruCache class (also available in the Support Li brary for use back to API Level 4) is very suitable for caching bitmaps. It uses LinkedHashMap, when the cache size is exceeded, it recycles the least recently used point.

Note: In the past, SoftReference or WeakReference was often used for caching, but it is not recommended now. From Android2.3 (API Level 9), the garbage collector rejects the use of them. After Android3.0 (11), bitmap is stored in a valid cache and cannot be released with predictable conditions. This will cause the memory to exceed the memory limit and cause a crash.
To select the appropriate memory space for LrcCache, the following factors should be taken seriously:
  • What is the free memory of your application?
  • How many images will be loaded at a time? How many images are displayed now?
  • What is the size and density of the screen? High-density is that, for example, Galaxy Nexus requires a larger cache than low-density devices.
  • What is the size and configuration of bitmap? What is the size of resources occupied by no image?
  • What kind of user experience do you need? Is there a need for a smooth experience? If so, you can keep them in the memory for a long time or use the LrcCache cache.
  • Do you need to choose between quality (memory size) and quality (image quality? Sometimes, we can choose to store thumbnails and load high-quality images in the background.
No memory of a specific size can be used by all applications. It depends on your analysis of memory usage to find a suitable solution. If the cache is too small, it makes no sense. If the cache is too large, it may cause java. lang. OutOfMemory exceptions and leave only a small part of the memory for your application. Here is an example of using LruCache:
private LruCache<String, Bitmap> mMemoryCache;@Overrideprotected 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);}
Note: In this example, one eighth of the application memory is allocated for our cache. on a normal/hdpi device this is a minimum of around 4 MB (32/8 ). A fullscreen GridView filled with images on a device with 800x480 resolution wocould use around 1.5 MB (800*480*4 bytes ), so this wocould cache a minimum of around 2.5 pages of images in memory.
When we load an image to ImageView, first check Lrucache. If you find the interface, we can quickly update the ImageView. Otherwise, we start a thread to load the image.
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;    }    ...}

Using the local cache memory cache is a good way to increase the speed, but you cannot rely on it completely. Like the GridView component, it quickly occupies a large amount of memory. Your application may be interrupted by other tasks, such as incoming calls, background processes may be terminated, and the memory cache may be released. When your application is started again, you have to reload. The local cache can solve this problem and help you store resources that are not required by the cache to reduce the number of times of repeated loading. Of course, the usage of the local cache is slower than that of the memory cache, to perform operations in the background, the read events are unpredictable. Note: A ContentProvider might be a more appropriate place to store cached images if they are accessed more frequently, for example in an image gallery application. the following code DiskLrcCache is an example of local loading:
private DiskLruCache mDiskLruCache;private final Object mDiskCacheLock = new Object();private boolean mDiskCacheStarting = true;private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MBprivate static final String DISK_CACHE_SUBDIR = "thumbnails";@Overrideprotected 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) {<pre name="code" class="java">private LruCache<String, Bitmap> mMemoryCache;@Overrideprotected void onCreate(Bundle savedInstanceState) {    ...    RetainFragment retainFragment =            RetainFragment.findOrCreateRetainFragment(getFragmentManager());    mMemoryCache = retainFragment.mRetainedCache;    if (mMemoryCache == null) {        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {            ... // Initialize cache here as usual        }        retainFragment.mRetainedCache = mMemoryCache;    }    ...}class RetainFragment extends Fragment {    private static final String TAG = "RetainFragment";    public LruCache<String, Bitmap> mRetainedCache;    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);    }}
Note: Even initializing the disk cache requires disk operations and therefore shocould not take place on the main thread. however, this does mean there's a chance the cache is accessed before initialization. to address this, in the above implementation, a lock object ensures that the app does not read from the disk cache until the cache has been initialized. the memory cache is detected in the UI thread. The local cache must be used in the background. The local cache cannot replace the memory cache in the UI thread. In the end, in order to use bitmap colleagues to be put into the memory and local to handle configuration changes in the runtime, for example, Changes in the screen direction will cause Android to destroy and restart Activity (For more information about this behavior, see Handling Runtime Changes). You want to avoid process your images again For a faster experience. Fortunately, you have a good memory cache mechanism, and you can use Fragment to ignore these 
setRetainInstance(true),When the Activity is re-created, the retained Fragment will also be re-attached to your application. The following is an example of how to handle configuration changes: 
class RetainFragment extends Fragment {    private static final String TAG = "RetainFragment";    public LruCache<String, Bitmap> mRetainedCache;    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);    }}
To test it, we can rotate the screen while retaining and not retaining Fragment. You should note that when you keep the Fragment, you will notice that the image will be loaded from the memory cache without any lag, and those not found in the memory will be cached to the local, if this is not the case, it is the same as general.

For the optimization of cached images, see the previous article: multi-thread processing Bitmaps



How to set the xUtils bitmap cache path

The BitmapUtils constructor parameters include.
 
C # using bitmap for dual buffering is ineffective. This is drawn on the panel's paint function.

What does this mean?

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.