Android caching technology

An excellent application has excellent user experience. Proper use of caching technology in Android applications can not only relieve server pressure, but also optimize user experience and reduce user traffic. There are two types of cache in Android: memory cache and disk cache:

Memory cache

  • Fast read speed
  • Small space available for allocation
  • System reclamation risks exist
  • You can’t do offline caching

    Disk cache

  • The read speed is slower than the memory cache
  • Large space to allocate
  • It will not be reclaimed by the system because of system memory shortage
  • This paper mainly introduces disk cache, and shows how to use disk cache to cache the news list by taking the news items of Zhihu daily in MVPDemo as examples.

DiskLruCache

DiskLruCache is an open source library on Github by JakeWharton, with a small amount of code. DiskLruCache corresponds to Google’s official memory caching policy LruCache. DiskLruCache also follows the LRU (Least Recently Used) algorithm, but is stored on disks. DiskLruCache is not integrated into the official API, although it is mentioned in the Google documentation. If it is, it is integrated into the Github library. Note when using DiskLruCache:

  • Each cache has a String key corresponding to it, and the value in each key must be satisfied[a - z0 - _ - 9] {1120}The rule is that the length of upper and lower case letters should be between 1 and 120. Therefore, it is recommended to use MD5 encryption for strings such as urls of images as keys.
  • The DiskLruCache data is cached in a directory on the file system. This directory must be unique to a cache. The cache may overwrite or delete files in the directory. An error occurs when multiple processes use the same cache directory at the same time.
  • DiskLruCache complies with the LRU algorithm. When the cache data reaches the preset limit, the console automatically removes the cache according to the LRU algorithm until the new cache does not exceed the limit.
  • A cached record can have only one Editor at a time, and a null value is returned if the value is not editable.
  • When a cache is created, the full value should be provided, with a placeholder if the value is empty.
  • If a file is lost from the file system, the corresponding entry is removed from the cache. If an error occurs while writing a cached value, editing will fail.

Method of use

Open the cache

DiskLruCache cannot be created using new. Create a cache object as follows:

/** ** Parameter Description ** cacheFile Path for storing cache files *appVersion Application version. DiskLruCache considers that all data should be pulled from the server after the application version is updated. Therefore, the version number is required to determine. *1 The number of values corresponding to each cache entry is set to 1. *Constants.CACHE_MAXSIZE The value in my own constant class represents the maximum storage space to be coarsed **/
DiskLruCache mDiskLruCache = DiskLruCache.open(cacheFile, appVersion, 1, Constants.CACHE_MAXSIZE);Copy the code

Deposited in the cache

The DiskLruCache cache is handled by DiskLruCache.Editor:

/ * * * here is a code, the actual use also need to try catch possible exception handling * * * /
String key = getMD5Result(key);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
OutputStream os = editor.newOutputStream(0);
// A news object is stored here so ObjectOutputStream is used
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(stories);
// Don't forget to close the stream and commit the edit
outputStream.close();
editor.commit();Copy the code

Remove the cache

DiskLruCache cache fetching is handled by DiskLruCache.Snapshot:

/ * * * here is a code, the actual use also need to try catch possible exception handling * * * /
String key = getMD5Result(key);
// Get the thumbnail object with the set key
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
// Get the stream data from the SnapShot object
InputStream in = snapshot.getInputStream(0);
ObjectInputStream ois = new ObjectInputStream(in);
// Convert stream data to Object
ArrayList<ZhihuStory> stories = (ArrayList<ZhihuStory>) ois.readObject();Copy the code

The basic process of disk caching with DiskLruCache is like this: open — > save or open — > fetch.

The code for the complete process

    // Use rxAndroid + Retrofit for the request
    public void loadDataByRxandroidRetrofit(a) {
        mINewsListActivity.showProgressBar();
        Subscription subscription = ApiManager.getInstence().getDataService()
                .getZhihuDaily()
                .map(new Func1<ZhiHuDaily, ArrayList<ZhihuStory>>() {
                    @Override
                    public ArrayList<ZhihuStory> call(ZhiHuDaily zhiHuDaily) {
                        ArrayList<ZhihuStory> stories = zhiHuDaily.getStories();
                        if(stories ! =null) {
                            // Cache data locally after successful loading (there is only one page in the demo. Choose whether to cache data based on actual requirements)
                            makeCache(zhiHuDaily.getStories());
                        }
                        returnstories; }})// Set the event to fire on the non-main thread
                .subscribeOn(Schedulers.io())
                // Set events to be accepted in the UI thread for the purpose of UI display
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<ArrayList<ZhihuStory>>() {
                    @Override
                    public void onCompleted(a) {
                        mINewsListActivity.hidProgressBar();
                    }

                    @Override
                    public void onError(Throwable e) {
                        mINewsListActivity.getDataFail("", e.getMessage());
                    }

                    @Override
                    public void onNext(ArrayList<ZhihuStory> stories) { mINewsListActivity.getDataSuccess(stories); }});Call presby.unsubcription () from ondestory or onpouse.
        addSubscription(subscription);
    }

    / / generated Cache
    private void makeCache(ArrayList<ZhihuStory> stories) {
        File cacheFile = getCacheFile(MyApplication.getContext(), Constants.ZHIHUCACHE);
        DiskLruCache diskLruCache = DiskLruCache.open(cacheFile, MyApplication.getAppVersion(), 1, Constants.CACHE_MAXSIZE);
        try {
            // Use the MD5 encrypted string as the key to avoid illegal characters in the key
            String key = SecretUtil.getMD5Result(Constants.ZHIHUSTORY_KEY);
            DiskLruCache.Editor editor = diskLruCache.edit(key);
            if(editor ! =null) {
                ObjectOutputStream outputStream = new ObjectOutputStream(editor.newOutputStream(0)); outputStream.writeObject(stories); outputStream.close(); editor.commit(); }}catch(IOException e) { e.printStackTrace(); }}/ / load the Cache
    public void loadCache(a) {
        File cacheFile = getCacheFile(MyApplication.getContext(), Constants.ZHIHUCACHE);
        DiskLruCache diskLruCache = DiskLruCache.open(cacheFile, MyApplication.getAppVersion(), 1, Constants.CACHE_MAXSIZE);
        String key = SecretUtil.getMD5Result(Constants.ZHIHUSTORY_KEY);
        try {
            DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
            if(snapshot ! =null) {
                InputStream in = snapshot.getInputStream(0);
                ObjectInputStream ois = new ObjectInputStream(in);
                try {
                    ArrayList<ZhihuStory> stories = (ArrayList<ZhihuStory>) ois.readObject();
                    if(stories ! =null) {
                        mINewsListActivity.getDataSuccess(stories);
                    } else {
                        mINewsListActivity.getDataFail(""."No data"); }}catch(ClassNotFoundException e) { e.printStackTrace(); }}}catch(IOException e) { e.printStackTrace(); }}// Get the Cache directory
    private  File getCacheFile(Context context, String uniqueName) {
        String cachePath = null;
        if((Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || ! Environment.isExternalStorageRemovable()) && context.getExternalCacheDir() ! =null) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + uniqueName);
    }Copy the code

The above code runs through the process Cache fetch Cache is no problem, but this is definitely not elegant! Two years ago, I would have written code like this as release code.

Method encapsulation, elegant use

What do you have in mind for both key and value and Editor? SharePreferences In MVPDemo I built a DiskLruCacheManager class to encapsulate Cache access. The DiskManager class can be used to access a Cache. The DiskManager class can be used to access a Cache.

Accessing the DiskManager is the same as accessing the DiskManager

DiskCacheManager manager = new DiskCacheManager(MyApplication.getContext(), Constants.ZHIHUCACHE);Copy the code

Data is then accessed via the manager’s public methods:

The data type In the method Take out the method instructions
String put(String key,String value) getString(String key) Mandatory String Object
JsonObject put(String key,JsonObject value) getJsonObject(String key) The internal is actually converted to String access
JsonArray put(String key,JsonArray value) getJsonArray(String key) The internal is actually converted to String access
byte[] put(String key,byte[] bytes) getBytes(String key) Save pictures with this implementation, we own packaging
Serializable put(String key,Serializable value) getSerializable(String key) It returns a generic object

The manager.flush() method is recommended to be called in the onPause () method of an interface that needs to be cached

The last

Find this article helpful for you to follow the simple book PandaQ404. Making the home page