You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 10:00:14