Retrofit 风格的 RxCache及其多种缓存替换算法_第1张图片 田园风光.jpg

RxCache 是一个支持 Java 和 Android 的 Local Cache 。

之前的文章《给 Java 和 Android 构建一个简单的响应式Local Cache》、《RxCache 整合 Android 的持久层框架 greenDAO、Room》曾详细介绍过它。

目前,对框架增加一些 Annotation 以及 Cache 替换算法。

一. 基于 Annotation 完成缓存操作

类似 Retrofit 风格的方式,支持通过标注 Annotation 来完成缓存的操作。

例如先定义一个接口,用于定义缓存的各种操作。

public interface Provider {    @CacheKey("user")    @CacheMethod(methodType = MethodType.GET)     Record getData(@CacheClass Class clazz);    @CacheKey("user")    @CacheMethod(methodType = MethodType.SAVE)    @CacheLifecycle(duration = 2000)    void putData(@CacheValue User user);    @CacheKey("user")    @CacheMethod(methodType = MethodType.REMOVE)    void removeUser();    @CacheKey("test")    @CacheMethod(methodType = MethodType.GET, observableType = ObservableType.MAYBE)     Maybe> getMaybe(@CacheClass Class clazz);}

通过 CacheProvider 创建该接口,然后可以完成各种缓存操作。

public class TestCacheProvider {    public static void main(String[] args) {        RxCache.config(new RxCache.Builder());        RxCache rxCache = RxCache.getRxCache();        CacheProvider cacheProvider = new CacheProvider.Builder().rxCache(rxCache).build();        Provider provider = cacheProvider.create(Provider.class);        User u = new User();        u.name = "tony";        u.password = "123456";        provider.putData(u); // 将u存入缓存中        Record record = provider.getData(User.class); // 从缓存中获取key="user"的数据        if (record!=null) {            System.out.println(record.getData().name);        }        provider.removeUser(); // 从缓存中删除key="user"的数据        record = provider.getData(User.class);        if (record==null) {            System.out.println("record is null");        }        User u2 = new User();        u2.name = "tony2";        u2.password = "000000";        rxCache.save("test",u2);        Maybe> maybe = provider.getMaybe(User.class); // 从缓存中获取key="test"的数据,返回的类型为Maybe        maybe.subscribe(new Consumer>() {            @Override            public void accept(Record userRecord) throws Exception {                User user = userRecord.getData();                if (user!=null) {                    System.out.println(user.name);                    System.out.println(user.password);                }            }        });    }}

CacheProvider 核心是 create(),它通过动态代理来创建Provider。

    public  T create(Class clazz) {        CacheProxy cacheProxy = new CacheProxy(rxCache);        try {            return (T) Proxy.newProxyInstance(CacheProvider.class.getClassLoader(), new Class[]{clazz}, cacheProxy);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }

其中,CacheProxy 实现了 InvocationHandler 接口,是创建代理类的调用处理器。

package com.safframework.rxcache.proxy;import com.safframework.rxcache.RxCache;import com.safframework.rxcache.proxy.annotation.*;import java.lang.annotation.Annotation;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** * Created by tony on 2018/10/30. */public class CacheProxy implements InvocationHandler {    RxCache rxCache;    public CacheProxy(RxCache rxCache) {        this.rxCache = rxCache;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        CacheMethod cacheMethod = method.getAnnotation(CacheMethod.class);        CacheKey cacheKey = method.getAnnotation(CacheKey.class);        CacheLifecycle cacheLifecycle = method.getAnnotation(CacheLifecycle.class);        Annotation[][] allParamsAnnotations = method.getParameterAnnotations();        Class cacheClazz = null;        Object cacheValue = null;        if (allParamsAnnotations != null) {            for (int i = 0; i < allParamsAnnotations.length; i++) {                Annotation[] paramAnnotations = allParamsAnnotations[i];                if (paramAnnotations != null) {                    for (Annotation annotation : paramAnnotations) {                        if (annotation instanceof CacheClass) {                            cacheClazz = (Class) args[i];                        }                        if (annotation instanceof CacheValue) {                            cacheValue = args[i];                        }                    }                }            }        }        if (cacheMethod!=null) {            MethodType methodType = cacheMethod.methodType();            long duration = -1;            if (cacheLifecycle != null) {                duration = cacheLifecycle.duration();            }            if (methodType == MethodType.GET) {                ObservableType observableType = cacheMethod.observableType();                if (observableType==ObservableType.NOUSE) {                    return  rxCache.get(cacheKey.value(),cacheClazz);                } else if (observableType == ObservableType.OBSERVABLE){                    return  rxCache.load2Observable(cacheKey.value(),cacheClazz);                } else if (observableType==ObservableType.FLOWABLE) {                    return  rxCache.load2Flowable(cacheKey.value(),cacheClazz);                } else if (observableType==ObservableType.SINGLE) {                    return  rxCache.load2Single(cacheKey.value(),cacheClazz);                } else if (observableType==ObservableType.MAYBE) {                    return  rxCache.load2Maybe(cacheKey.value(),cacheClazz);                }            } else if (methodType == MethodType.SAVE) {                rxCache.save(cacheKey.value(),cacheValue,duration);            } else if (methodType == MethodType.REMOVE) {                rxCache.remove(cacheKey.value());            }        }        return null;    }}

CacheProxy 的 invoke() 方法先获取 Method 所使用的 Annotation,包括CacheMethod、CacheKey、CacheLifecycle。

其中,CacheMethod 是最核心的 Annotation,它取决于 rxCache 使用哪个方法。CacheMethod 支持的方法类型包括:获取、保存、删除缓存。当 CacheMethod 的 methodType 是 GET 类型,则可能会返回 RxJava 的各种 Observable 类型,或者还是返回所存储的对象类型。

CacheKey 是任何方法都需要使用的 Annotation。CacheLifecycle 只有保存缓存时才会使用。

二. 支持多种缓存替换算法

RxCache 包含了两级缓存: Memory 和 Persistence 。

Memory 的默认实现 FIFOMemoryImpl、LRUMemoryImpl、LFUMemoryImpl 分别使用 FIFO、LRU、LFU 算法来缓存数据。

2.1 FIFO

通过使用 LinkedList 存放缓存的 keys,ConcurrentHashMap 存放缓存的数据,就可以实现 FIFO。

2.2 LRU

LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的。

使用 ConcurrentHashMap 和 ConcurrentLinkedQueue 实现该算法。如果某个数据已经存放在缓存中,则从 queue 中删除并添加到 queue 的第一个位置。如果缓存已满,则从 queue 中删除最后面的数据。并把新的数据添加到缓存。

public class LRUCache {    private Map cache = null;    private AbstractQueue queue = null;    private int size = 0;    public LRUCache() {        this(Constant.DEFAULT_CACHE_SIZE);    }    public LRUCache(int size) {        this.size = size;        cache = new ConcurrentHashMap(size);        queue = new ConcurrentLinkedQueue();    }    public boolean containsKey(K key) {        return cache.containsKey(key);    }    public V get(K key) {        //Recently accessed, hence move it to the tail        queue.remove(key);        queue.add(key);        return cache.get(key);    }    public V getSilent(K key) {        return cache.get(key);    }    public void put(K key, V value) {        //ConcurrentHashMap doesn't allow null key or values        if(key == null || value == null) throw new RxCacheException("key is null or value is null");        if(cache.containsKey(key)) {            queue.remove(key);        }        if(queue.size() >= size) {            K lruKey = queue.poll();            if(lruKey != null) {                cache.remove(lruKey);            }        }        queue.add(key);        cache.put(key,value);    }    /**     * 获取最近最少使用的值     * @return     */    public V getLeastRecentlyUsed() {        K remove = queue.remove();        queue.add(remove);        return cache.get(remove);    }    public void remove(K key) {        cache.remove(key);        queue.remove(key);    }    public void clear() {        cache.clear();        queue.clear();    }    ......}

2.3 LFU

LFU是Least Frequently Used的缩写,即最近最不常用使用。

看上去跟 LRU 类似,其实它们并不相同。LRU 是淘汰最长时间未被使用的数据,而 LFU 是淘汰一定时期内被访问次数最少的数据。

LFU 会记录数据在一定时间内的使用次数。稍显复杂感兴趣的可以阅读 RxCache 中相关的源码。

三. 总结

RxCache 大体已经完成,初步可以使用。

RxCache github 地址:https://github.com/fengzhizi715/RxCache
Android 版本的 RxCache github 地址:https://github.com/fengzhizi715/RxCache4a

对于 Android ,除了支持常见的持久层框架之外,还支持 RxCache 转换成 LiveData。如果想要跟 Retrofit 结合,可以通过 RxCache 的 transform 策略。

对于Java 后端,RxCache 只是一个本地缓存,不适合存放大型的数据。但是其内置的 Memory 层包含了多种缓存替换算法,不用内置的 Memory 还可以使用 Guava Cache、Caffeine 。

更多相关文章

  1. Android Webservices 返回多行多列数据(Dataset)
  2. Android 如何导入已有的外部数据库
  3. Android性能优化-数据优化
  4. Android通过http协议数据交互的两种方式
  5. Android在API推荐的方式来实现SQLite数据库的增长、删除、变化、
  6. Android 通过Post方式提交数据
  7. android将对象写入文件和从文件中读取对象数据
  8. android 向SD卡写入数据
  9. Android之常见数据类型

随机推荐

  1. 常用代码合集一
  2. Android缩放动画
  3. Android(安卓)dumpsys 命令解析
  4. 如何查看Android设备的CPU架构信息
  5. Android实现图片帮助跳转以及选择重拍Sql
  6. Android开机启动流程说明
  7. Some useful Android(安卓)components
  8. android&&TableLayout
  9. Android下载网络图片到本地
  10. Flutter 图片保存到本地