首页 > 解决方案 > 获取 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;

标签: javaandroidandroid-sqlite

解决方案


我建议更改您的代码以使用:-

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。每个游标在其下方都是一个文件,这里的限制是可以一次打开的文件(文件句柄)的数量(因为其他打开的文件会影响打开游标的最大数量)。因此,当您完成游标时,您应该始终关闭它们(从您给出的答案中您似乎意识到了这一点)。在您的代码中,永远不会关闭空光标,这可能会导致打开太多光标。我展示的代码只会在应用程序崩溃时关闭光标,在这种情况下打开的光标将被关闭。再次使用建议的代码总是关闭光标,因此采用这个或基于建议的代码可以消除这个问题的发生。


推荐阅读