美文网首页
数据缓存

数据缓存

作者: 夜沐下的星雨 | 来源:发表于2020-08-24 11:38 被阅读0次

    为什么数据缓存?
    在使用网络的情况下,我们都是在获取服务器数据,那么当无网络后,我们在此打开页面都会缓存第一次的首要数据。
    思路:
    创建重写MvpRepository
    使用双检索单列模式
    将数据存储到对象中,通过HashMap()进行内存缓存 key :对应的栏目id ,value: 该栏目的数据

     * OkHttp 网络请求:
     * map:负责把json 串转成对象  把对象包装到response上
     * doOnNext :插入一个做耗时操作  :
     * 可以:
     * 做数据缓存 在子线程   拿到MvpResponse对象    有一个Recommend对象
    
     * IO 线程    把线程切入到工作线程
     * 主线程  :负责把线程切入到主线程
     * compose :制定生命周期执行时接触订阅关系
     * observer: 观察者
    
    
      推荐页的请求方式:
     *          1.第一次  Fragment 创建时(启动)请求
     *                   a.冷启动App 时:
     *                              i: 内存里面的缓存肯定没有,尝试加载Sdcard,如果scard没有就请求服务器(数据回来后,存储内存和sdcard缓存),如果有,在子线程里面去读取sdcard缓存的数据,(可以放入内存中)然后主线程里面回调给P层
     *                      b.ViewPager切换时导致Fragment 销毁重建时
     *                                i:从内存里面读取,把上一次该栏目请求的所有的数据,回调给P层,比如你在推荐栏目加载(loading)了5页数据,然后你切换到请求栏目,这个时候推荐Fragment 是不是已经销毁了,再次回到推荐是,应该恢复到上次销毁之前的状态(5页数据不变)。为了实现这个效果。必须对每个栏目的数据做缓存
     *                       如果缓存没有:从服务器加载,加载回来写入缓存:
     *                                 内存缓存: 清空缓存(本来就没有)
     *          2.刷新:下拉时请求
     *                       a.肯定不能读任何缓存,刷新成功后,把对应栏目的缓存清空,用新的数据替换,不管是内存还是sdcard
     *                                i: 内存缓存:清空缓存,放入新的
     *                                ii: 替换原来的缓存
     *           3.加载更多: 上拉时请求
     *                        a.也不能读缓存,加载成功后,往对应的栏目的缓存里面的newsList 里面追加数据(还需要把服务器返回的start,nmber,pointTitem 赋值给缓存对应的对象   为了页面切换是,从缓存里面读取数据时加载更多时能衔接上),同时还有替换sdcard里面的缓存。
     *              缓存规则:
     *                                    内存替换
     *                                     sdcard:替换
    
      准备工作:
     * 1.定义一个内存缓存的数据结构  hashMap<String ,RecommendData>
     *  2.写一个工具类:
     *                   a.用于缓存对象(把对象转出 json, 加密写入文件)
     *                   b.用于读取缓存对象(把加密后的解密成 json,并把json 转出我们需要的对象)
    
    

    创建RecommondRepository 继承BaseRepository 用来进行网络缓存

    public class RecommondRepository extends BaseRepository {
        private static final String CACHE_FILE_NAME_PREFIX = "version_code";
        private static RecommondRepository mInstance;
        private HashMap<String, RecommendData> mMemoryData = new HashMap<>();
    
        /**
         *
         * @return
         * 单例是的只有一个model对象
         */
        public static RecommondRepository getInstance() {
    //        判断对象是否创建
            if (mInstance == null) {
    //            在此处创建线程索让其他线程减少等待时间
                synchronized (RecommondRepository.class) {
    //                如果线程1拿到锁对象未创建完成,线程2拿到锁创建完对象线程1继续创建此时对象已经通过线程2实例化所以进行二次判空
                    if (mInstance == null) {
    //                    创建对象
                        mInstance = new RecommondRepository();
    
                    }
                }
            }
    
            return mInstance;
        }
    
        @Override
        public <T> void doRequest(LifecycleProvider lifecycleProvider, MvpRequest<T> request, IBaseCallBack<T> callBack) {
            /*取数据*/
    //        不调用父类的方法,重写子类
    //        super.doRequest(lifecycleProvider, request, callBack);
    //        得到栏目ID
            String columnId = request.getParams().get(Constract.RequestKey.KEY_COLUMN_ID).toString();
    
            switch (request.getRequestType()) {
    
    //            case加入括号{}可以重复创建变量局部变量只能在方法里有效
    /**
     * 默认都是第一次请求
     * 刷新和加载更多都不需要读取缓存数据
     */
                case FIRST: {
    //                step1.先从内从存查找直接传入P层
                    MvpResponse response = getFromMemory(columnId);
                    if (response != null) {
                        callBack.onResult(response);
                        return;
    
                    }
    //             step2.如果内存没有直接从sdcard读取
    
    
                    /*
                     *Observable 内置线程池节省系统开销
                     * scared都为耗时操作为 */
                    Observable.create(new ObservableOnSubscribe<MvpResponse>() {
                        @Override
                        public void subscribe(@NonNull ObservableEmitter<MvpResponse> emitter) throws Throwable {
                            MvpResponse fromSdCard = getFromSdCard(columnId);
                            if (fromSdCard != null) {
                                emitter.onNext(fromSdCard);
                                emitter.onComplete();
                            } else {
                                emitter.onError(new NullPointerException("sdcard no data"));
                            }
                        }
                    })
                            .subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe(new Consumer<MvpResponse>() {
                                @Override
                                public void accept(MvpResponse mvpResponse) throws Throwable {
                                    callBack.onResult(mvpResponse);
                                }
                            }, new Consumer<Throwable>() {
                                @Override
                                public void accept(Throwable throwable) throws Throwable {
                                    doRequest(lifecycleProvider, request, callBack, new CacheTask<>(request));
                                }
                            });
    
                    break;
                }
                default:{
    //                从服务请求
    
                    doRequest(lifecycleProvider,request,callBack,new CacheTask<>(request));
                }
            }
    
        }
    //  读取内存数据
        private MvpResponse getFromMemory(String key) {
    //        看内存是否有该数据
            RecommendData data = mMemoryData.get(key);
            if (data != null) {
    //            设置数据
                MvpResponse<RecommendData> response = new MvpResponse<>();
                return response.setData(data).setCode(1).setRequestType(RequestType.FIRST).setType(ResponseType.MEMORY);
            }
            return null;
        }
    
        /**
         * @param key
         * @return 从sd卡读取文件
         */
        private MvpResponse getFromSdCard(String key) {
    //        从内存卡读取数据
            RecommendData data = MvpDataFileCacheUtils.getencryptedDataFromFile(getCacheDataSdcardFile(key), RecommendData.class);
            if (data != null) {
                MvpResponse<RecommendData> response = new MvpResponse<>();
                return response.setCode(1).setData(data).setType(ResponseType.SDCARD);
            }
            return null;
        }
    
        /**
         * @param columnId
         * @return 创建手机文件
         */
    //    找到文件
        private File getCacheDataSdcardFile(String columnId) {
            return new File(MvpManager.getExternalCacheDir(), CACHE_FILE_NAME_PREFIX + columnId);
        }
    
    //  缓存数据
        private class CacheTask<T> implements Consumer<MvpResponse<T>> {//实现做缓存接口
    
            private MvpRequest request;
    
            CacheTask(MvpRequest request) {
                this.request = request;
            }
    
            @SuppressWarnings("ALL")
            @Override
            public void accept(MvpResponse<T> mvpResponse) throws Throwable {
    //            得到保存的Key值
                String key = request.getParams().get(Constract.RequestKey.KEY_COLUMN_ID).toString();
                saveToMemory((MvpResponse<RecommendData>) mvpResponse, request.getRequestType(), key);
                saveToSdcard((MvpResponse<RecommendData>) mvpResponse, key);
            }
    //         保存到card
            private void saveToSdcard(MvpResponse<RecommendData> mvpResponse, String key) {
    
            }
    //      保存到内存
            private void saveToMemory(MvpResponse<RecommendData> mvpResponse, RequestType requestType, String key) {
                if (requestType != RequestType.LOAD_MORE){//不是加载更多
                    mMemoryData.clear();//将内存设置空
                    RecommendData recommendData = new RecommendData();//创建空对象开辟内存
                    RecommendData serverData = mvpResponse.getData();//从网络获取data
    //                设置数据
                    recommendData.setAlbumId(serverData.getAlbumId());
                    recommendData.setAlbumNews(serverData.getAlbumNews());
                    recommendData.setAlbumTitle(serverData.getAlbumTitle());
                    recommendData.setBannerList(serverData.getBannerList());
                    recommendData.setFlashId(serverData.getFlashId());
                    recommendData.setFlashNews(serverData.getFlashNews());
                    recommendData.setMore(serverData.getMore());
                    recommendData.setPointTime(serverData.getPointTime());
                    recommendData.setStart(serverData.getStart());
                    recommendData.setNews(new ArrayList<>(serverData.getNews()));
    //                缓存到集合
                    mMemoryData.put(key,recommendData);
                }else {
                    RecommendData cacheData = mMemoryData.get(key);//获取缓存加入数据
    
                    RecommendData serverData = mvpResponse.getData();//获取网络数据
    //                如果网络数据和缓存数据都不为空
                    if(cacheData != null && serverData != null){
    //                    所有数据只有新闻数据需要实施gen新
                        cacheData.setStart(serverData.getStart());
                        cacheData.setNumber(serverData.getNumber());
                        cacheData.setPointTime(serverData.getPointTime());
                        cacheData.setMore(serverData.getMore());
                        cacheData.getNews().addAll(serverData.getNews());
                    }
                }
            }
        }
    

    对sd卡的数据进行加密:

    a.
    public class MvpDataFileCacheUtils {
        private static final String SECRET_KEY = "tnKqXmiVZFYXIit9uYmnrg==";
        //
    
        /**
         * 把 json 转出指定的对象并返回
         *
         * @param tClass  需要转的对象的class
         * @param jsonStr json 串
         */
        public static <T> T convertToDataFromJson(Class<T> tClass, String jsonStr) {
            if (TextUtils.isEmpty(jsonStr)) {
                return null;
            }
            Gson gson = new Gson();
            return gson.fromJson(jsonStr, tClass);
        }
        /**
         * 把 json 转出指定的对象List<T> 这种类型并返回,比如 List<Person>
         *
         * @param tClass  :List里面泛型的class,比如 Person.class
         * @param jsonStr : json 串
         */
    
        public static <T> List<T> convertToListFromJson(Class<T> tClass, String jsonStr) {
    
            if (TextUtils.isEmpty(jsonStr)) {
                return null;
            }
            Gson gson = new Gson();
            ParameterizedTypeImpl parameterizedType = new ParameterizedTypeImpl(List.class, new Type[]{tClass});
    
            return gson.fromJson(jsonStr, parameterizedType);
        }
        /**
         * Map<String,Map<String,List<String>>>
         * <p>
         * ParameterizedTypeImpl listType = new ParameterizedTypeImpl(List.class,new Type[]{String.class}); // List<String>
         * <p>
         * ParameterizedTypeImpl mapInnerType = new ParameterizedTypeImpl(Map.class,new Type[]{String.class,listType}); // Map<String,List<String>
         * <p>
         * ParameterizedTypeImpl parameterizedType = new ParameterizedTypeImpl(Map.class,new Type[]{String.class,mapInnerType}); Map<String,Map<String,List<String>>
         */
        public static <K, V> Map<K, V> convertToMapFromJson(Class<K> keyClass, Class<V> vclass, String jsonStr) {
    
            if (TextUtils.isEmpty(jsonStr)) {
                return null;
            }
            Gson gson = new Gson();
            ParameterizedTypeImpl parameterizedType = new ParameterizedTypeImpl(Map.class, new Type[]{keyClass, vclass});
            return gson.fromJson(jsonStr, parameterizedType);
        }
        // 把一个对象转出json
        public static String convertToJsonFromData(Object data) {
            if (data == null) return null;
            Gson gson = new Gson();
            return gson.toJson(data);
        }
        /**
         * 把一个对象保存到文件里面,
         *
         * @param file 保存文件
         * @param data 需要保存的对象
         */
        public static void saveDataToFile(File file, Object data) {
    
            String json = convertToJsonFromData(data);
    
            if (TextUtils.isEmpty(json)) {
                return;
            }
    
            FileOutputStream outputStream = null;
            try {
                outputStream = new FileOutputStream(file);
    
                outputStream.write(json.getBytes("utf-8"));
    
    
                outputStream.flush();
    
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
        }
    
        public static void saveEncryptedDataToFile(File file, Object data) {
    
            String json = convertToJsonFromData(data);
    
            // 加密
            if (TextUtils.isEmpty(json)) {
                return;
            }
            FileOutputStream outputStream = null;
            try {
                json = EncryptUtils.encrypt(SECRET_KEY,json); // 加密
                outputStream = new FileOutputStream(file);
                outputStream.write(json.getBytes("utf-8"));
                outputStream.flush();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        /**
         * 从文件里面读取出对象
         *
         * @param file
         * @param tClass
         * @param <T>
         * @return
         */
    
        public static <T> T getencryptedDataFromFile(File file, Class<T> tClass) {
            String json= readFromFile(file);
            try {
                json = EncryptUtils.decrypt(SECRET_KEY,json);// 解密
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
            return convertToDataFromJson(tClass, json);
        }
        public static <T> T getDataFromFile(File file, Class<T> tClass) {
            return convertToDataFromJson(tClass, readFromFile(file));
        }
        /**
         * 从文件里面读取出对象,这个对象是一个List<T>
         *
         * @param file
         * @param tClass
         * @param <T>
         * @return
         */
        public static <T> List<T> getListFromFile(File file, Class<T> tClass) {
    
            return convertToListFromJson(tClass, readFromFile(file));
        }
        private static String readFromFile(File file) {
            if (file == null || !file.exists()) {
                return null;
            }
            BufferedReader reader = null;
            try {
                FileInputStream fileInputStream = new FileInputStream(file);
                reader = new BufferedReader(new InputStreamReader(fileInputStream, "UTF-8"));
                int c;
                StringBuilder buffer = new StringBuilder();
                while ((c = reader.read()) != -1) {
                    buffer.append((char) c);
                }
                return buffer.toString();
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            return null;
        }
        public static class ParameterizedTypeImpl implements ParameterizedType {
            // List<Column>
            private final Class raw; // List.class
            private final Type[] args; // {Column.class}
            public ParameterizedTypeImpl(Class raw, Type[] args) {
                this.raw = raw;
                this.args = args;
            }
    
            @NonNull
            @Override
            public Type[] getActualTypeArguments() {
                return args;// { Column.class}
            }
    
            @NonNull
            @Override
            public Type getRawType() {
                return raw; // List.class
            }
    
            @Nullable
            @Override
            public Type getOwnerType() {
                return null;
            }
        }
    
    
    }
    
    
    b
    
    /**
     * @desc: AES对称加密,对明文进行加密、解密处理
     * @author: yong.li
     * @createTime: 2018年8月28日 上午9:54:52
     * @version: v0.0.1
     */
    public class EncryptUtils {
        private static final String KEY_ALGORITHM = "AES";
        private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
    
        /**
         * @desc: AES对称-加密操作
         * @author: yong.li
         * @createTime: 2018年8月28日 上午10:25:50
         * @version: v0.0.1
         * @param keyStr 进行了Base64编码的秘钥
         * @param data 需要进行加密的原文
         * @return String 数据密文,加密后的数据,进行了Base64的编码
         */
        public static String encrypt(String keyStr, String data) throws Exception {
            // 转换密钥
            Key key = new SecretKeySpec(Base64.decode(keyStr,Base64.DEFAULT), KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            // 加密
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] result = cipher.doFinal(data.getBytes());
    
            return Base64.encodeToString(result,Base64.DEFAULT);
        }
    
        /**
         * @desc: AES对称-解密操作
         * @author: yong.li
         * @createTime: 2018年8月28日 上午10:22:47
         * @version: v0.0.1
         * @param keyStr 进行了Base64编码的秘钥
         * @param data 需要解密的数据<span style="color:red;">(数据必须是通过AES进行加密后,对加密数据Base64编码的数据)</span>
         * @return String 返回解密后的原文
         */
        public static String decrypt(String keyStr, String data) throws Exception {
            // 转换密钥
            Key key = new SecretKeySpec(Base64.decode(keyStr,Base64.DEFAULT), KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            // 解密
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] result = cipher.doFinal(Base64.decode(data,Base64.DEFAULT));
            return new String(result);
        }
    
    
    
        /**
         * @desc: 生成AES的秘钥,秘钥进行了Base64编码的字符串
         * @author: yong.li
         * @createTime: 2018年8月28日 上午10:24:32
         * @version: v0.0.1
         * @return String 对生成的秘钥进行了Base64编码的字符串
         */
        public static String keyGenerate(String key) throws Exception {
            // 生成密钥
            KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
            keyGenerator.init(128,new SecureRandom(key.getBytes()));
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] keyBytes = secretKey.getEncoded();
            return Base64.encodeToString(keyBytes,Base64.DEFAULT);
        }
    }
    

    相关文章

      网友评论

          本文标题:数据缓存

          本文链接:https://www.haomeiwen.com/subject/woewjktx.html