java - 获取 android.database.CursorWindowAllocationException 频繁崩溃
问题描述
已经探索过其他主题,但有同样的例外。就我而言,我已经在适当的时间关闭了光标。因此从if (cursor != null && cursor.getCount() > 0)中得到这个异常很少见。请找到以下代码,并需要您对此案例的专家建议。
List<User> itemList = new ArrayList<>();
String selectQuery = "SELECT * FROM " + TABLE_USERS;
try (SQLiteDatabase db = this.getWritableDatabase()) {
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor != null && cursor.getCount() > 0) {
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
User item = new User();
item.setId(cursor.getInt(0));
item.setUserPhoneNumber(cursor.getString(1));
itemList.add(item);
cursor.moveToNext();
}
}
cursor.close();
}
} catch (SQLiteException ex) {
Log.e(TAG, ex.toString());
}
return itemList;
解决方案
我建议更改您的代码以使用:-
List<User> itemList = new ArrayList<>();
String selectQuery = "SELECT * FROM " + TABLE_USERS;
SQLiteDatabase db = this.getWritableDatabase()
Cursor cursor = db.rawQuery(selectQuery, null);
while (cursor.moveToNext) {
User item = new user();
item.setId(cursor.getint(0));
item.setUserPhoneNumber(cursor.getString(1));
itemList.add(item);
}
csr.close();
return itemList;
- 该代码是原则性代码,未经检查、编译或运行,因此可能包含错误。
- 不建议捕获 SQLiteExceptions。
- 从任何 SQliteDatabase 方法检查游标是否为空都是徒劳的,因为不会返回空游标。
- 无需检查所有光标移动的计数???如果无法进行移动,则方法返回 false,因此 while(your_cursor.moveToNext()) { .... } 将正确循环所有行。
光标窗口分配错误通常是由于没有足够的内存可用。最常见的原因是图像存储在数据库中。
- 由于存储数据不受光标窗口(基本上是缓冲区)的限制,因此可以将图像插入数据库但随后无法检索它。
- 光标窗口所需的内存取决于 Android 版本。我相信它可以是1,2或4M。
- 除非使用非标准方法,否则任何超过 4M 的图像都会导致失败。即使那样,也可能会产生明显的影响。
- 预计任何 1M 或更大的图像的潜在问题。
- 如果图像高达 100-200k,则存储它们可能是有益的(即使每个光标窗口最多只有 10-5 个)
- 建议将图像存储为文件,并将图像的路径存储在数据库中。
如果上述更改导致失败,那么(甚至在运行上述更改之前)。我建议将查询更改为仅返回所需的列(id 和 phoneNumber)。所以而不是
SELECT * FROM ....
- .... 未按原样编码,它代表原始代码的其余部分。
使用(列名组成,因为它们未在问题中显示,因此您需要替换实际的列名)
SELECT id_column_name, phoneNumber_column_name FROM ....
这将减少每行所需的内存量。光标窗口必须能够容纳至少 1 行。
堆栈跟踪将包含更多关于错误的信息,例如所需大小和可用大小。如果上述方法没有帮助,请编辑您的问题以包含堆栈跟踪。您不妨参考https://developer.android.com/studio/debug/
额外的
另一个潜在的问题是打开了太多的游标。最大数量约为 1000。每个游标在其下方都是一个文件,这里的限制是可以一次打开的文件(文件句柄)的数量(因为其他打开的文件会影响打开游标的最大数量)。因此,当您完成游标时,您应该始终关闭它们(从您给出的答案中您似乎意识到了这一点)。在您的代码中,永远不会关闭空光标,这可能会导致打开太多光标。我展示的代码只会在应用程序崩溃时关闭光标,在这种情况下打开的光标将被关闭。再次使用建议的代码总是关闭光标,因此采用这个或基于建议的代码可以消除这个问题的发生。
推荐阅读
- deep-learning - 半监督 CNN 模型训练的 Google Colab RAM 问题
- html - 如何放置带有视频背景的导航栏?
- javascript - 如何使用 laravel mix 使用公共 API?
- angular - TS7053:元素隐式具有“任何”类型,因为“页面”类型的表达式不能用于索引类型“对象”
- android - 从firebase实时数据库检索到的问题解组对象:第一个字段未更新
- c# - CreateLogger() 之前被调用过,只能调用一次
- laravel - Livewire:显示数据和表单的问题
- cypress - Cypress.run 设置与 zone.js 不同的标头请求
- hibernate - 有没有办法使用 JPA Criteria API/JPQL SELECT COUNT (DISTINCT col1 || col2)?
- sql - 我正在使用 Oracle SQL Developer 并希望从时间戳中提取星期几。我的语法是否正确?