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

Android从SQLite生成大JSONArray时如何避免OutOfMemoryError?

解决SQLite生成大JSONArray时的OutOfMemoryError问题

原代码的核心问题是一次性将所有查询结果加载到内存中的JSONArray,当数据量达到80-100MB时,JSON对象的内存开销会远大于原始数据大小,直接触发内存溢出。以下是适配大数据集的优化方案:

方案1:流式输出JSON(推荐)

直接将数据逐行写入输出流(比如文件或网络流),内存中仅保留当前处理的单条数据,彻底避免内存堆积。

public void writeJsonToFile(File outputFile) throws IOException {
    String myPath = context.getDatabasePath(DATABASE_NAME).getPath();
    FileWriter writer = new FileWriter(outputFile);

    try (SQLiteDatabase myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY)) {
        Cursor cursor = myDataBase.rawQuery("SELECT * FROM " + MY_TABLE, null);
        String[] columnNames = cursor.getColumnNames();

        // 写入JSON数组开头
        writer.write("[");
        boolean firstItem = true;

        if (cursor.moveToFirst()) {
            do {
                if (!firstItem) {
                    writer.write(",");
                }
                firstItem = false;

                JSONObject jsonObjectNotes = new JSONObject();
                for (int i = 0; i < columnNames.length; i++) {
                    String columnName = columnNames[i];
                    try {
                        String value = cursor.getString(i);
                        jsonObjectNotes.put(columnName, value != null ? value : "");
                    } catch (Exception e) {
                        Log.d("error", e.getMessage());
                    }
                }
                // 将当前对象写入流
                writer.write(jsonObjectNotes.toString());
                // 主动释放当前对象内存(可选,帮助GC)
                jsonObjectNotes = null;
            } while (cursor.moveToNext());
        }

        // 写入JSON数组结尾
        writer.write("]");
        cursor.close();
    } finally {
        writer.flush();
        writer.close();
    }
}

方案2:分批处理数据

通过SQL的LIMIT和OFFSET分页查询,每次仅加载固定数量的数据到内存,处理完成后再加载下一批,适合需要对数据做中间处理的场景。

public void processLargeDataInBatches(int batchSize) {
    String myPath = context.getDatabasePath(DATABASE_NAME).getPath();
    int offset = 0;

    try (SQLiteDatabase myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY)) {
        while (true) {
            String query = "SELECT * FROM " + MY_TABLE + " LIMIT ? OFFSET ?";
            Cursor cursor = myDataBase.rawQuery(query, new String[]{String.valueOf(batchSize), String.valueOf(offset)});

            if (cursor.getCount() == 0) {
                cursor.close();
                break;
            }

            String[] columnNames = cursor.getColumnNames();
            JSONArray batchArray = new JSONArray();

            if (cursor.moveToFirst()) {
                do {
                    JSONObject jsonObjectNotes = new JSONObject();
                    for (int i = 0; i < columnNames.length; i++) {
                        String columnName = columnNames[i];
                        try {
                            String value = cursor.getString(i);
                            jsonObjectNotes.put(columnName, value != null ? value : "");
                        } catch (Exception e) {
                            Log.d("error", e.getMessage());
                        }
                    }
                    batchArray.put(jsonObjectNotes);
                } while (cursor.moveToNext());
            }

            // 处理当前批次的数据(比如写入文件、上传等)
            handleBatchData(batchArray);

            cursor.close();
            offset += batchSize;
            // 主动触发GC(可选)
            System.gc();
        }
    }
}

// 示例:处理单批次数据的方法
private void handleBatchData(JSONArray batchArray) {
    // 这里可以将批次数据写入文件、发送到服务器等
    Log.d("batch", "处理了" + batchArray.length() + "条数据");
}

额外优化点

  • 提前获取列名数组:原代码每次循环调用cursor.getColumnName(i),可以提前用cursor.getColumnNames()获取数组,减少重复调用的开销。
  • 避免不必要的空值判断:cursor.getColumnName(i)不会返回null,可以去掉该判断。
  • 关闭资源:确保Cursor和数据库连接都被正确关闭(原代码已经用try-with-resources处理数据库连接,Cursor也有close,这点保持即可)。

内容的提问来源于stack exchange,提问作者Zhiliand

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 04:12:29