Android ROOM数据库流式读取与JSON导出内存优化求助
解决Room导出JSON内存过高的流式方案
核心思路是分批读取Room数据+JSON流式写入,避免一次性加载全量数据到内存,同时直接将JSON内容写入文件,不生成完整的JSON字符串。以下是具体实现方案:
1. Room分页查询数据
不要一次性拉取全表数据,而是通过LIMIT和OFFSET分批查询,直到获取不到数据为止。
首先在Dao中添加分页查询方法:
@Dao interface YourEntityDao { // 分页查询,每次返回limit条数据 @Query("SELECT * FROM your_entity_table LIMIT :limit OFFSET :offset") suspend fun getEntitiesByPage(limit: Int, offset: Int): List<YourEntity> // 获取总条数,用于判断是否还有下一页 @Query("SELECT COUNT(*) FROM your_entity_table") suspend fun getTotalCount(): Int }
2. 用Gson流式写入JSON
Gson提供JsonWriter类,可以直接向文件输出流写入JSON内容,无需在内存中构建完整的JSON结构。
示例代码:
import com.google.gson.Gson import com.google.gson.stream.JsonWriter import java.io.File import java.io.FileOutputStream import java.io.OutputStreamWriter suspend fun exportToJson(file: File, dao: YourEntityDao) { val pageSize = 1000 // 每页数据量,可根据单条数据大小调整 val totalCount = dao.getTotalCount() var offset = 0 // 使用try-with-resources自动关闭流 FileOutputStream(file).use { fos -> OutputStreamWriter(fos, Charsets.UTF_8).use { osw -> JsonWriter(osw).use { writer -> writer.beginArray() // 开始JSON数组 val gson = Gson() while (offset < totalCount) { val entities = dao.getEntitiesByPage(pageSize, offset) entities.forEach { entity -> // 直接将实体写入JSON流 gson.toJson(entity, YourEntity::class.java, writer) } offset += pageSize } writer.endArray() // 结束JSON数组 } } } }
3. 用Jackson流式写入(可选,性能更优)
如果追求更高的序列化性能,可以使用Jackson的JsonGenerator,同样支持流式写入:
import com.fasterxml.jackson.databind.ObjectMapper import java.io.File import java.io.FileOutputStream suspend fun exportToJsonWithJackson(file: File, dao: YourEntityDao) { val pageSize = 1000 val totalCount = dao.getTotalCount() var offset = 0 val mapper = ObjectMapper() FileOutputStream(file).use { fos -> mapper.createGenerator(fos).use { generator -> generator.writeStartArray() while (offset < totalCount) { val entities = dao.getEntitiesByPage(pageSize, offset) entities.forEach { entity -> mapper.writeValue(generator, entity) } offset += pageSize } generator.writeEndArray() } } }
关键优化点
- 分页大小调整:根据单条实体的数据大小调整
pageSize,比如单条数据较大时设为500,较小时设为2000,平衡内存占用和数据库查询次数。 - 事务保证一致性:如果导出过程中数据库可能被修改,给分页查询方法加上
@Transaction注解,确保导出的是数据库的一致快照:@Transaction @Query("SELECT * FROM your_entity_table LIMIT :limit OFFSET :offset") suspend fun getEntitiesByPage(limit: Int, offset: Int): List<YourEntity> - 避免主线程操作:导出操作属于IO密集型任务,一定要放在协程或子线程中执行,不要阻塞主线程。
内容的提问来源于stack exchange,提问作者saas
相关产品推荐
相关产品推荐

