内容简介:之前的文章RxCache 包含了两级缓存: Memory 和 Persistence 。
一. 背景
RxCache 是一个支持 Java 和 Android 的 Local Cache 。
之前的文章 给 Java 和 Android 构建一个简单的响应式Local Cache 曾详细介绍过它。
RxCache 包含了两级缓存: Memory 和 Persistence 。
下图是 rxcache-core 模块的 uml 类图
二. 持久层
RxCache 的持久层包括 Disk、DB,分别单独抽象了 Disk、DB 接口并继承 Persistence。
DB 接口:
package com.safframework.rxcache.persistence.db;
import com.safframework.rxcache.persistence.Persistence;
/**
* Created by tony on 2018/10/14.
*/
public interface DB extends Persistence {
}
复制代码
在 RxCache 的持久层,尝试集成 Android 常用的持久层框架。
2.1 集成 greenDAO
greenDAO 是一款开源的面向 Android 的轻便、快捷的 ORM 框架,将 Java 对象映射到 SQLite 数据库。
首先,创建一个缓存实体 CacheEntity ,它包含 id、key、data、timestamp、expireTime。其中 data 是待缓存的对象并转换成 json 字符串。
@Entity
public class CacheEntity {
@Id(autoincrement = true)
private Long id;
public String key;
public String data;// 对象转换的 json 字符串
public Long timestamp;
public Long expireTime;
...... // getter 、setter
}
复制代码
创建一个单例的 DBService ,并提供返回 CacheEntityDao 的方法。其实,crud 的逻辑也可以放在此处。
public class DBService {
private static final String DB_NAME = "cache.db";
private static volatile DBService defaultInstance;
private DaoSession daoSession;
private DBService(Context context) {
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(context, DB_NAME);
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
daoSession = daoMaster.newSession();
}
public static DBService getInstance(Context context) {
if (defaultInstance == null) {
synchronized (DBService.class) {
if (defaultInstance == null) {
defaultInstance = new DBService(context.getApplicationContext());
}
}
}
return defaultInstance;
}
public CacheEntityDao getCacheEntityDao(){
return daoSession.getCacheEntityDao();
}
}
复制代码
创建 GreenDAOImpl 实现 DB 接口,实现真正的缓存逻辑。
import com.safframework.rxcache.config.Constant;
import com.safframework.rxcache.domain.Record;
import com.safframework.rxcache.domain.Source;
import com.safframework.rxcache.persistence.converter.Converter;
import com.safframework.rxcache.persistence.converter.GsonConverter;
import com.safframework.rxcache.persistence.db.DB;
import com.safframework.tony.common.utils.Preconditions;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
* @FileName: com.safframework.rxcache4a.persistence.db.greendao.GreenDAOImpl
* @author: Tony Shen
* @date: 2018-10-15 11:50
* @version: V1.0 <描述当前版本功能>
*/
public class GreenDAOImpl implements DB {
private CacheEntityDao dao;
private Converter converter;
public GreenDAOImpl(CacheEntityDao dao) {
this(dao,new GsonConverter());
}
public GreenDAOImpl(CacheEntityDao dao, Converter converter) {
this.dao = dao;
this.converter = converter;
}
@Override
public <T> Record<T> retrieve(String key, Type type) {
CacheEntity entity = dao.queryBuilder().where(CacheEntityDao.Properties.Key.eq(key)).unique();
if (entity==null) return null;
long timestamp = entity.timestamp;
long expireTime = entity.expireTime;
T result = null;
if (expireTime<0) { // 缓存的数据从不过期
String json = entity.data;
result = converter.fromJson(json,type);
} else {
if (timestamp + expireTime > System.currentTimeMillis()) { // 缓存的数据还没有过期
String json = entity.data;
result = converter.fromJson(json,type);
} else { // 缓存的数据已经过期
evict(key);
}
}
return result != null ? new Record<>(Source.PERSISTENCE, key, result, timestamp, expireTime) : null;
}
@Override
public <T> void save(String key, T value) {
save(key,value, Constant.NEVER_EXPIRE);
}
@Override
public <T> void save(String key, T value, long expireTime) {
if (Preconditions.isNotBlanks(key,value)) {
CacheEntity entity = new CacheEntity();
entity.setKey(key);
entity.setTimestamp(System.currentTimeMillis());
entity.setExpireTime(expireTime);
entity.setData(converter.toJson(value));
dao.save(entity);
}
}
@Override
public List<String> allKeys() {
List<CacheEntity> list = dao.loadAll();
List<String> result = new ArrayList<>();
for (CacheEntity entity:list) {
result.add(entity.key);
}
return result;
}
@Override
public boolean containsKey(String key) {
List<String> keys = allKeys();
return Preconditions.isNotBlank(keys) ? keys.contains(key) : false;
}
@Override
public void evict(String key) {
CacheEntity entity = dao.queryBuilder().where(CacheEntityDao.Properties.Key.eq(key)).unique();
if (entity!=null) {
dao.delete(entity);
}
}
@Override
public void evictAll() {
dao.deleteAll();
}
}
复制代码
2.2 集成 Room
Room 是 Google 开发的一个 SQLite 对象映射库。 使用它来避免样板代码并轻松地将 SQLite 数据转换为 Java 对象。 Room 提供 SQLite 语句的编译时检查,可以返回 RxJava 和 LiveData Observable。
同样,需要先创建一个 CacheEntity,但是不能共用之前的 CacheEntity。因为 Room、greenDAO 使用的 @Entity 不同。
@Entity
public class CacheEntity {
@PrimaryKey(autoGenerate = true)
private Long id;
public String key;
public String data;// 对象转换的 json 字符串
public Long timestamp;
public Long expireTime;
...... // getter 、setter
}
复制代码
创建一个 CacheEntityDao 用于 crud 的实现。
import java.util.List;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import static androidx.room.OnConflictStrategy.IGNORE;
/**
* @FileName: com.safframework.rxcache4a.persistence.db.room.CacheEntityDao
* @author: Tony Shen
* @date: 2018-10-15 16:44
* @version: V1.0 <描述当前版本功能>
*/
@Dao
public interface CacheEntityDao {
@Query("SELECT * FROM cacheentity")
List<CacheEntity> getAll();
@Query("SELECT * FROM cacheentity WHERE `key` = :key LIMIT 0,1")
CacheEntity findByKey(String key);
@Insert(onConflict = IGNORE)
void insert(CacheEntity entity);
@Delete
void delete(CacheEntity entity);
@Query("DELETE FROM cacheentity")
void deleteAll();
}
复制代码
创建一个 AppDatabase 表示一个数据库的持有者。
import androidx.room.Database;
import androidx.room.RoomDatabase;
/**
* @FileName: com.safframework.rxcache4a.persistence.db.room.AppDatabase
* @author: Tony Shen
* @date: 2018-10-15 16:40
* @version: V1.0 <描述当前版本功能>
*/
@Database(entities = {CacheEntity.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract CacheEntityDao cacheEntityDao();
}
复制代码
最后,创建 RoomImpl 实现 DB 接口,实现真正的缓存逻辑。
import android.content.Context;
import com.safframework.rxcache.config.Constant;
import com.safframework.rxcache.domain.Record;
import com.safframework.rxcache.domain.Source;
import com.safframework.rxcache.persistence.converter.Converter;
import com.safframework.rxcache.persistence.converter.GsonConverter;
import com.safframework.rxcache.persistence.db.DB;
import com.safframework.tony.common.utils.Preconditions;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import androidx.room.Room;
/**
* @FileName: com.safframework.rxcache4a.persistence.db.room.RoomImpl
* @author: Tony Shen
* @date: 2018-10-15 16:46
* @version: V1.0 <描述当前版本功能>
*/
public class RoomImpl implements DB {
private AppDatabase db;
private Converter converter;
private static final String DB_NAME = "cache";
public RoomImpl(Context context) {
this(context,new GsonConverter());
}
public RoomImpl(Context context, Converter converter) {
this.db = Room.databaseBuilder(context, AppDatabase.class, DB_NAME).build();
this.converter = converter;
}
@Override
public <T> Record<T> retrieve(String key, Type type) {
CacheEntity entity = db.cacheEntityDao().findByKey(key);
if (entity==null) return null;
long timestamp = entity.timestamp;
long expireTime = entity.expireTime;
T result = null;
if (expireTime<0) { // 缓存的数据从不过期
String json = entity.data;
result = converter.fromJson(json,type);
} else {
if (timestamp + expireTime > System.currentTimeMillis()) { // 缓存的数据还没有过期
String json = entity.data;
result = converter.fromJson(json,type);
} else { // 缓存的数据已经过期
evict(key);
}
}
return result != null ? new Record<>(Source.PERSISTENCE, key, result, timestamp, expireTime) : null;
}
@Override
public <T> void save(String key, T value) {
save(key,value, Constant.NEVER_EXPIRE);
}
@Override
public <T> void save(String key, T value, long expireTime) {
if (Preconditions.isNotBlanks(key,value)) {
CacheEntity entity = new CacheEntity();
entity.setKey(key);
entity.setTimestamp(System.currentTimeMillis());
entity.setExpireTime(expireTime);
entity.setData(converter.toJson(value));
db.cacheEntityDao().insert(entity);
}
}
@Override
public List<String> allKeys() {
List<CacheEntity> list = db.cacheEntityDao().getAll();
List<String> result = new ArrayList<>();
for (CacheEntity entity:list) {
result.add(entity.key);
}
return result;
}
@Override
public boolean containsKey(String key) {
List<String> keys = allKeys();
return Preconditions.isNotBlank(keys) ? keys.contains(key) : false;
}
@Override
public void evict(String key) {
CacheEntity entity = db.cacheEntityDao().findByKey(key);
if (entity!=null) {
db.cacheEntityDao().delete(entity);
}
}
@Override
public void evictAll() {
db.cacheEntityDao().deleteAll();
}
}
复制代码
这两种集成方式,都使用 CacheEntity 的 data 来存储对象转换后的 json 字符串。使用这种方式,可以替换成任何的持久层框架。使得 DB 也可以成为 RxCache 的其中一级缓存。
三. 使用
编写单元测试,看一下集成 greenDAO 的效果。
分别测试多种对象的存储、带 ExpireTime 的存储。
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import com.safframework.rxcache.RxCache;
import com.safframework.rxcache.domain.Record;
import com.safframework.rxcache4a.persistence.db.greendao.CacheEntityDao;
import com.safframework.rxcache4a.persistence.db.greendao.DBService;
import com.safframework.rxcache4a.persistence.db.greendao.GreenDAOImpl;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* @FileName: com.safframework.rxcache4a.GreenDAOImplTest
* @author: Tony Shen
* @date: 2018-10-15 18:51
* @version: V1.0 <描述当前版本功能>
*/
@RunWith(AndroidJUnit4.class)
public class GreenDAOImplTest {
Context appContext;
DBService dbService;
@Before
public void setUp() {
appContext = InstrumentationRegistry.getTargetContext();
dbService = DBService.getInstance(appContext);
}
@Test
public void testWithObject() {
CacheEntityDao dao = dbService.getCacheEntityDao();
GreenDAOImpl impl = new GreenDAOImpl(dao);
impl.evictAll();
RxCache.config(new RxCache.Builder().persistence(impl));
RxCache rxCache = RxCache.getRxCache();
Address address = new Address();
address.province = "Jiangsu";
address.city = "Suzhou";
address.area = "Gusu";
address.street = "ren ming road";
User u = new User();
u.name = "tony";
u.password = "123456";
u.address = address;
rxCache.save("user",u);
Record<User> record = rxCache.get("user", User.class);
assertEquals(u.name, record.getData().name);
assertEquals(u.password, record.getData().password);
assertEquals(address.city, record.getData().address.city);
rxCache.save("address",address);
Record<Address> record2 = rxCache.get("address", Address.class);
assertEquals(address.city, record2.getData().city);
}
@Test
public void testWithExpireTime() {
CacheEntityDao dao = dbService.getCacheEntityDao();
GreenDAOImpl impl = new GreenDAOImpl(dao);
impl.evictAll();
RxCache.config(new RxCache.Builder().persistence(impl));
RxCache rxCache = RxCache.getRxCache();
User u = new User();
u.name = "tony";
u.password = "123456";
rxCache.save("test",u,2000);
try {
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Record<User> record = rxCache.get("test", User.class);
assertNull(record);
}
}
复制代码
两个 test case 都顺利通过,表示集成 greenDAO 没有问题。当然,集成 Room 也是一样。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- SSM框架整合
- Vue应用框架整合与实战
- Swoole 整合成一个小框架
- 【Java】SSM框架整合 附源码
- 用 Docker 整合測試 Flutter 框架
- Next框架与主流工具的整合(二)—— 完善与优化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Game Programming Patterns
Robert Nystrom / Genever Benning / 2014-11-2 / USD 39.95
The biggest challenge facing many game programmers is completing their game. Most game projects fizzle out, overwhelmed by the complexity of their own code. Game Programming Patterns tackles that exac......一起来看看 《Game Programming Patterns》 这本书的介绍吧!