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
相关产品推荐
相关产品推荐

