数据缓存

2020-08-24  本文已影响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);
    }
}
上一篇下一篇

猜你喜欢

热点阅读