深入浅出安卓K线缓存实践
什么是K线数据?
K线(蜡烛图)是金融交易中最常用的图表类型之一,它显示了一段时间内的开盘价、收盘价、最高价和最低价。在股票、外汇、加密货币等交易应用中,K线数据是核心展示内容。
为什么需要缓存K线数据?
- 减少网络请求:K线数据通常从服务器获取,频繁请求会增加服务器负担和用户流量消耗
- 提升用户体验:本地缓存可以实现秒开,避免每次等待网络加载
- 离线访问:在网络不稳定或离线时仍能查看历史数据
- 节省成本:减少API调用次数,特别是付费API
安卓K线缓存实现方案
1. 内存缓存(一级缓存)
// 使用LruCache实现内存缓存
private LruCache<String, List<KLineItem>> memoryCache;
// 初始化
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8; // 使用最大内存的1/8
memoryCache = new LruCache<String, List<KLineItem>>(cacheSize) {
@Override
protected int sizeOf(String key, List<KLineItem> value) {
// 计算每条K线数据占用的内存大小
return value.size() * 20; // 估算值,根据实际数据结构调整
}
});
// 存入缓存
memoryCache.put(cacheKey, kLineData);
// 读取缓存
List<KLineItem> cachedData = memoryCache.get(cacheKey);
2. 磁盘缓存(二级缓存)
使用Room数据库实现持久化缓存:
// 定义K线数据实体
@Entity(tableName = "kline_data")
public class KLineEntity {
@PrimaryKey
public String symbol; // 交易对
public String interval; // 时间间隔(1m,15m,1h等)
public long timestamp; // 时间戳
public double open; // 开盘价
public double close; // 收盘价
public double high; // 最高价
public double low; // 最低价
public double volume; // 成交量
}
// 定义DAO接口
@Dao
public interface KLineDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<KLineEntity> entities);
@Query("SELECT * FROM kline_data WHERE symbol = :symbol AND interval = :interval ORDER BY timestamp ASC")
List<KLineEntity> getKLineData(String symbol, String interval);
@Query("DELETE FROM kline_data WHERE symbol = :symbol AND interval = :interval")
void deleteBySymbolAndInterval(String symbol, String interval);
}
// 使用示例
AppDatabase db = Room.databaseBuilder(context, AppDatabase.class, "kline-db").build();
KLineDao kLineDao = db.kLineDao();
// 保存数据
kLineDao.insertAll(entities);
// 读取数据
List<KLineEntity> cachedData = kLineDao.getKLineData("BTCUSDT", "1h");
3. 缓存策略设计
public class KLineCacheManager {
// 获取K线数据的完整流程
public Observable<List<KLineItem>> getKLineData(String symbol, String interval, long startTime, long endTime) {
// 1. 先从内存缓存获取
String memoryKey = generateMemoryKey(symbol, interval, startTime, endTime);
List<KLineItem> memoryData = memoryCache.get(memoryKey);
if (memoryData != null) {
return Observable.just(memoryData);
}
// 2. 从磁盘缓存获取
return Observable.fromCallable(() -> {
List<KLineEntity> diskData = kLineDao.getKLineData(symbol, interval);
if (diskData != null && !diskData.isEmpty()) {
List<KLineItem> convertedData = convertToKLineItems(diskData);
// 存入内存缓存
memoryCache.put(memoryKey, convertedData);
return convertedData;
}
return null;
}).flatMap(diskData -> {
if (diskData != null) {
return Observable.just(diskData);
}
// 3. 从网络请求
return apiService.getKLineData(symbol, interval, startTime, endTime)
.doOnNext(networkData -> {
// 保存到磁盘
List<KLineEntity> entities = convertToEntities(networkData, symbol, interval);
kLineDao.insertAll(entities);
// 保存到内存
memoryCache.put(memoryKey, networkData);
});
});
}
// 生成内存缓存key
private String generateMemoryKey(String symbol, String interval, long startTime, long endTime) {
return symbol + "_" + interval + "_" + startTime + "_" + endTime;
}
}
4. 缓存更新策略
-
时间过期策略:
- 短期K线(1m/5m/15m):缓存5-10分钟
- 中期K线(1h/4h):缓存1-2小时
- 长期K线(1d/1w):缓存24小时
-
增量更新:
- 只请求最新时间段的数据,与本地缓存合并
- 避免全量刷新
// 增量更新示例
public void updateKLineData(String symbol, String interval) {
// 获取本地最新数据的时间
long latestTime = getLatestCachedTime(symbol, interval);
// 只请求最新时间之后的数据
apiService.getKLineData(symbol, interval, latestTime, System.currentTimeMillis())
.subscribe(newData -> {
// 合并新旧数据
List<KLineItem> mergedData = mergeData(cachedData, newData);
// 更新缓存
memoryCache.put(generateMemoryKey(symbol, interval), mergedData);
kLineDao.insertAll(convertToEntities(newData, symbol, interval));
});
}
优化技巧
-
数据压缩:
- 使用Protocol Buffers或FlatBuffers替代JSON
- 对数值进行精度处理(如价格保留4位小数)
-
分片缓存:
- 按时间范围分片存储,避免加载全部历史数据
- 实现按需加载
-
智能预加载:
- 根据用户浏览习惯预加载可能查看的K线数据
- 在WiFi环境下预加载更多数据
-
内存优化:
- 使用基本类型数组替代对象列表
- 对不再显示的数据及时释放内存
常见问题解决
-
数据不一致:
- 实现版本控制或时间戳校验
- 提供手动刷新按钮
-
缓存膨胀:
- 设置最大缓存大小
- 定期清理过期数据
-
多周期同步:
- 当1小时K线更新时,同步更新4小时和日K数据
- 建立K线数据间的关联关系
总结
安卓K线缓存实现需要结合内存缓存和磁盘缓存,设计合理的缓存策略和更新机制。通过多级缓存、智能预加载和数据压缩等技术,可以在保证数据及时性的同时,大幅提升应用性能和用户体验。实际开发中,还需要根据具体业务需求和数据特点进行调整优化。