如何强制关闭 CursorWindow 以便我发出 1MB 的请求,清空 CursorWinow 并继续发出其他请求?
我不认为关闭光标是您的问题,就好像不关闭光标会附加到光标并展开一样。
您的问题在于您构建的查询。
总之substr函数不是from to,它是from for(for 是返回字符串的大小/长度。您的计算基于第二个值是字符的偏移量)。因此,提取的字符串的长度会增加块大小,直到它超过字符串的末尾(在此之前吹过 CursorWindow)。
因此,使用 1MB 的第二个块(如果被视为使用偏移量)在第二次运行时注定会失败,因为它实际上是要提取的长度 (2MB)。减少到小于 1MB 将允许一些余地,但可能会破坏 CursorWindow(但会获得额外的数据)。
但是,作为替代方案,它使用单个游标,每个块作为提取行。解决方案可能是:-
//Load in chunks
BookDbHelper bookDbHelper = new BookDbHelper(/*GlobalContext.get()*/this);
SQLiteDatabase readableDatabase = bookDbHelper.getReadableDatabase();
//Query length
StringBuilder wholeBookText = new StringBuilder();
int chunk_size = (int) Math.pow(2, 20);//mb
String query_length = "SELECT length(text) FROM " + BookContract.TABLE_NAME + " WHERE _id=?";
Cursor cursor = readableDatabase.rawQuery(query_length, new String[]{String.valueOf(id)});
int length = 0;
if (cursor.moveToFirst()) {
length = cursor.getInt(0);
}
int numSteps = length / chunk_size + 1;
int i = 0;
Log.d("BOOKINFO", "Length of Text is " + length + " Number of Chunks = " + numSteps + " Chunk Size = " + chunk_size);
StringBuilder sb = new StringBuilder();
for (i=1; i < length + 1; i+= chunk_size) {
if (sb.length() > 1) sb.append(" UNION ALL ");
sb.append("SELECT substr(text,")
.append(String.valueOf(i)).append(",").append(String.valueOf(chunk_size))
.append(") FROM ").append(BookContract.TABLE_NAME)
.append(" WHERE _id=").append(String.valueOf(id));
}
sb.append(";");
Log.d("BOOKINFOV2","SQL generated :-\n\t" + sb.toString());
cursor = readableDatabase.rawQuery(sb.toString(),null);
wholeBookText = new StringBuilder();
while (cursor.moveToNext()) {
wholeBookText.append(cursor.getString(0));
Log.d("BOOKINFO","Obtained String who's length is " + cursor.getString(0).length() + "\n\tTotal Extracted = " + wholeBookText.length());
}
而不是单个查询循环运行。这会生成一个查询,将每个块提取为一行。也就是说,它在所有查询之间建立了一个 UNION。例如
SELECT substr(text,1,1048576) FROM book WHERE _id=4
UNION ALL SELECT substr(text,1048577,1048576) FROM book WHERE _id=4
UNION ALL SELECT substr(text,2097153,1048576) FROM book WHERE _id=4
UNION ALL SELECT substr(text,3145729,1048576) FROM book WHERE _id=4;
- 取自上述测试运行。
- 可以看出,to(应该是for)是块大小。最后一个chunk会根据剩余数据截断。
测试运行的完整输出:-
2019-12-16 14:21:35.546 D/BOOKINFOV2: SQL generated :-
SELECT substr(text,1,1048576) FROM book WHERE _id=4 UNION ALL SELECT substr(text,1048577,1048576) FROM book WHERE _id=4 UNION ALL SELECT substr(text,2097153,1048576) FROM book WHERE _id=4 UNION ALL SELECT substr(text,3145729,1048576) FROM book WHERE _id=4;
2019-12-16 14:21:35.555 W/CursorWindow: Window is full: requested allocation 1048577 bytes, free space 1048128 bytes, window size 2097152 bytes
2019-12-16 14:21:35.585 D/BOOKINFO: Obtained String who's length is 1048576
Total Extracted = 1048576
2019-12-16 14:21:35.599 W/CursorWindow: Window is full: requested allocation 1048577 bytes, free space 1048128 bytes, window size 2097152 bytes
2019-12-16 14:21:35.616 D/BOOKINFO: Obtained String who's length is 1048576
Total Extracted = 2097152
2019-12-16 14:21:35.653 D/BOOKINFO: Obtained String who's length is 1048576
Total Extracted = 3145728
2019-12-16 14:21:35.654 D/BOOKINFO: Obtained String who's length is 51
Total Extracted = 3145779
- 如您所见,CursorWindow 会溢出,但未添加该行,下次添加并访问它。
当然,您可以采用多查询方法,在这种情况下,代码可以是:-
//Load in chunks
BookDbHelper bookDbHelper = new BookDbHelper(/*GlobalContext.get()*/this);
SQLiteDatabase readableDatabase = bookDbHelper.getReadableDatabase();
//Query length
StringBuilder wholeBookText = new StringBuilder();
int chunk_size = (int) Math.pow(2, 19);//mb
chunk_size = (1024 * 1024);
String query_length = "SELECT length(text) FROM " + BookContract.TABLE_NAME + " WHERE _id=?";
Cursor cursor = readableDatabase.rawQuery(query_length, new String[]{String.valueOf(id)});
int length = 0;
if (cursor.moveToFirst()) {
length = cursor.getInt(0);
}
int numSteps = length / chunk_size + 1;
int i = 0;
Log.d("BOOKINFO", "Length of Text is " + length + " Number of Chunks = " + numSteps + " Chunk Size = " + chunk_size);
int from = 1, to = chunk_size;
while (i < numSteps && length > 0) {
if (to > length) to = length;
String query = "SELECT substr(text," + from + "," + (chunk_size) + ") FROM " + BookContract.TABLE_NAME + " WHERE _id=?";
Log.d("BOOKINFOSQL",query);
cursor.close();
cursor = readableDatabase.rawQuery(query, new String[]{String.valueOf(id)});
//Read
if (cursor.moveToFirst()) {
wholeBookText.append(cursor.getString(0));
Log.d("BOOKINFO","Obtained String who's length is " + cursor.getString(0).length() + "\n\tTotal Extracted = " + wholeBookText.length());
}
cursor.close();
i++;
from = (i * chunk_size) + 1;
to = from + chunk_size;
}
if (!cursor.isClosed()) {
cursor.close();
}
Log.d("BOOKINFO", "The length of the extracted data is " + wholeBookText.length());
以上结果:-
2019-12-16 14:16:15.336 D/BOOKINFO: Length of Text is 3145779 Number of Chunks = 4 Chunk Size = 1048576
2019-12-16 14:16:15.336 D/BOOKINFOSQL: SELECT substr(text,1,1048576) FROM book WHERE _id=?
2019-12-16 14:16:15.358 D/BOOKINFO: Obtained String who's length is 1048576
Total Extracted = 1048576
2019-12-16 14:16:15.358 D/BOOKINFOSQL: SELECT substr(text,1048577,1048576) FROM book WHERE _id=?
2019-12-16 14:16:15.382 D/BOOKINFO: Obtained String who's length is 1048576
Total Extracted = 2097152
2019-12-16 14:16:15.383 D/BOOKINFOSQL: SELECT substr(text,2097153,1048576) FROM book WHERE _id=?
2019-12-16 14:16:15.409 D/BOOKINFO: Obtained String who's length is 1048576
Total Extracted = 3145728
2019-12-16 14:16:15.409 D/BOOKINFOSQL: SELECT substr(text,3145729,1048576) FROM book WHERE _id=?
2019-12-16 14:16:15.418 D/BOOKINFO: Obtained String who's length is 51
Total Extracted = 3145779
2019-12-16 14:16:15.418 D/BOOKINFO: The length of the extracted data is 3145779