安卓笔记侠

专注安卓开发

导航

一种简单粗暴的数据层网络缓存

先贴出代码:

  1 package com.cache;
  2  
  3 import android.content.Context;
  4 import android.text.TextUtils;
  5 import android.util.Log;
  6  
  7 import com.google.gson.Gson;
  8  
  9 import java.util.ArrayList;
 10 import java.util.List;
 11 import java.util.concurrent.CopyOnWriteArrayList;
 12 import java.util.concurrent.ExecutorService;
 13 import java.util.concurrent.Executors;
 14  
 15 public class CacheHelper {
 16  
 17     private static final String TAG = "CacheHelper";
 18  
 19     private static final String SHARED_PREFERENCES_NAME = "web_content_cache";
 20     private static final String STORE_KEY_XXX = "STORE_KEY_XXX";
 21  
 22     private static final boolean NEED_LIMIT_MAX_CACHE_LENGTH = true;
 23     private static final int MAX_CACHE_LENGTH = 500000;
 24  
 25     private static CacheHelper sInstance;
 26     private ExecutorService mExecutorService;
 27  
 28     public static synchronized CacheHelper instance() {
 29         if (null == sInstance) {
 30             sInstance = new CacheHelper();
 31         }
 32         return sInstance;
 33     }
 34  
 35     private CacheHelper() {
 36         mExecutorService = Executors.newSingleThreadExecutor();
 37  
 38     }
 39  
 40     /**
 41      * 缓存某个场景下的数据。
 42      *
 43      * @param cacheValue
 44      */
 45     public synchronized void cacheXXXList(final List<XXXBean> cacheValue) {
 46         runCache(cacheValue, STORE_KEY_XXX);
 47     }
 48  
 49     /**
 50      * 获取某个场景下的数据缓存,回调在工作线程中执行。(如果有UI操作,请注意需要在post回UI线程执行。)
 51      *
 52      * @param callback
 53      */
 54     public synchronized void getCacheXXXList(final CacheXXXCallback callback) {
 55         if (null == callback) {
 56             return;
 57         }
 58         // 通过某种全局的Utils得到Context,如果没有这种方法,需要在获取实例的时候传入Context
 59         final Context appContext = AppUtils.getContext();
 60         if (null == appContext) {
 61             return;
 62         }
 63         checkThreadPool();
 64         mExecutorService.execute(new Runnable() {
 65             @Override
 66             public void run() {
 67                 // 目前使用SharedPreference存储Json String,可以改成文件等其他方式
 68                 final String cacheStr = appContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
 69                         Context.MODE_PRIVATE).getString(STORE_KEY_XXX, null);
 70                 final List<XXXBean> cacheValue = new CopyOnWriteArrayList<>();
 71                 if (!TextUtils.isEmpty(cacheStr)) {
 72                     final Gson gson = new Gson();
 73                     final List<String> jsons = gson.fromJson(cacheStr, List.class);
 74                     for (String item : jsons) {
 75                         cacheValue.add(gson.fromJson(item, XXXBean.class));
 76                     }
 77                 }
 78                 callback.onGetXXXList(cacheValue);
 79             }
 80         });
 81     }
 82  
 83     public static interface CacheXXXCallback {
 84         public void onGetXXXList(List<XXXBean> cacheValue);
 85     }
 86  
 87     private <T> void runCache(final List<T> list, final String cacheKey) {
 88         // 通过某种全局的Utils得到Context,如果没有这种方法,需要在获取实例的时候传入Context
 89         final Context appContext = AppUtils.getContext();
 90         if (null == appContext) {
 91             return;
 92         }
 93         if (null == list || list.isEmpty()) {
 94             return;
 95         }
 96         final CopyOnWriteArrayList<T> copyList = new CopyOnWriteArrayList<>();
 97         copyList.addAll(list);
 98         if (copyList.isEmpty()) {
 99             return;
100         }
101         checkThreadPool();
102         mExecutorService.execute(new Runnable() {
103             @Override
104             public void run() {
105                 final Gson gson = new Gson();
106                 final ArrayList<String> jsons = new ArrayList<>();
107                 for (T t : copyList) {
108                     jsons.add(gson.toJson(t));
109                 }
110                 final String cacheStr = gson.toJson(jsons);
111                 if (NEED_LIMIT_MAX_CACHE_LENGTH) {
112                     if (cacheStr.length() > MAX_CACHE_LENGTH) {
113                         Log.e(TAG, "runCache() : cache value length over max limit.");
114                         return;
115                     }
116                 }
117                 // 目前使用SharedPreference存储Json String,可以改成文件等其他方式
118                 appContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
119                         .edit().putString(cacheKey, cacheStr).commit();
120             }
121         });
122     }
123  
124     private void checkThreadPool() {
125         if (null == mExecutorService || mExecutorService.isShutdown()
126                 || mExecutorService.isTerminated()) {
127             mExecutorService = Executors.newSingleThreadExecutor();
128         }
129     }
130 }
说明几个地方:
(1)在helper类内部用线程池实现了异步。使用异步的原因第一,存储Json String的方式有可能是文件,有可能是SharedPreference,涉及到IO;第二,可能会频繁调用写cache和读cache,异步化可以降低调用线程的压力,另外在单一的工作线程(单一线程的线程池)中顺序执行读、写,实现同步。
(2)通过测试发现,对于数据实体对象,Gson无法将对象的List直接转成Json字符串,但大多数的应用场景都是List,所以采用先将List中的每一个对象转成Json String,组成一个List<String>,然后再将List<String>转成一个Json String,存储。在读缓存的时候,是以上过程的逆过程,先将存储的Json String转成List<String>,然后将List中的每一个String转成一个数据实体对象。
(3)存储的方式目前使用的是SharedPreference。关于SharedPreference在这个场景的使用,稍后有时间会有另一篇文章细聊。存储方式可以替换成文件等,只要将读写操作稍作抽象即可,不影响数据转化的逻辑。

posted on 2017-09-23 15:25  安卓笔记侠  阅读(563)  评论(0编辑  收藏  举报