
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)<T> Record<T> getData(@CacheClass Class<T> 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)<T> Maybe<Record<T>> getMaybe(@CacheClass Class<T> 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<User> 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<Record<User>> maybe = provider.getMaybe(User.class); // 从缓存中获取key="test"的数据,返回的类型为Maybemaybe.subscribe(new Consumer<Record<User>>() {@Overridepublic void accept(Record<User> 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> T create(Class<T> 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;}@Overridepublic 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<K,V> {private Map<K,V> cache = null;private AbstractQueue<K> queue = null;private int size = 0;public LRUCache() {this(Constant.DEFAULT_CACHE_SIZE);}public LRUCache(int size) {this.size = size;cache = new ConcurrentHashMap<K,V>(size);queue = new ConcurrentLinkedQueue<K>();}public boolean containsKey(K key) {return cache.containsKey(key);}public V get(K key) {//Recently accessed, hence move it to the tailqueue.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 valuesif(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 。
关注【Java与Android技术栈】
更多精彩内容请关注扫码:

RxCache:高级缓存管理
RxCache是一款针对Java和Android的本地缓存框架,支持通过注解进行缓存操作,如获取、保存和删除缓存。框架内含多种缓存替换算法,包括FIFO、LRU和LFU,适用于不同的应用场景。
416

被折叠的 条评论
为什么被折叠?



