2012-07-05 37 views
54

我有一个例程每秒多次对SQLite数据库运行不同的查询。一段时间后,我会得到错误SQLite Android数据库光标窗口分配2048 kb失败

"android.database.CursorWindowAllocationException: - Cursor window allocation of 2048 kb failed. # Open Cursors = "出现在LogCat中。

我有应用程序日志内存使用情况,实际上当使用量达到一定的限制时,我得到这个错误,意味着它用完了。我的直觉告诉我,每次运行查询时,数据库引擎都会创建一个NEW缓冲区(CursorWindow),即使我标记了.close()游标,垃圾回收器和内存释放都不够快。我认为解决方案可能在于“迫使”数据库始终写入相同的缓冲区,而不是创建新的数据库,但我一直无法找到一种方法来做到这一点。我试图实例化我自己的CursorWindow,并试图设置它和SQLiteCursor无济于事。

¿有什么想法?

编辑:重新从@GrahamBorland示例代码的请求:

public static CursorWindow cursorWindow = new CursorWindow("cursorWindow"); 
public static SQLiteCursor sqlCursor; 
public static void getItemsVisibleArea(GeoPoint mapCenter, int latSpan, int lonSpan) { 
query = "SELECT * FROM Items"; //would be more complex in real code 
sqlCursor = (SQLiteCursor)db.rawQuery(query, null); 
sqlCursor.setWindow(cursorWindow); 
} 

我非常希望能够.setWindow()给人一种新的查询之前,并有投入相同CursorWindow每次我得到新的数据数据。

+0

我不知道是什么问题.. :)但我使用,使SQLiteOpenHelper类单。所以我从来没有发现任何这样的问题。 –

+0

不,我不使用SQLiteOpenHelper,我创建了一个包含SQLiteDatabase的静态DataAccess类。这工作正常,我怀疑问题在那里。这个问题更多的与SQLite库创建一个NEW容器来放置每个新查询的结果,而不是一次又一次地使用同一个容器。尽管我可以关闭光标,但是GC清理的速度比创建新容器的速度慢,因此会产生内存宏。 – alex

+0

你可以显示一些你的代码,特别是你想要设置自己的'CursorWindow'吗? –

回答

88

这个错误的原因通常是非关闭游标。确保在使用它们后关闭所有游标(即使出现错误)。

Cursor cursor = null; 
try { 
    cursor = db.query(... 
    // do some work with the cursor here. 
} finally { 
    // this gets called even if there is an exception somewhere above 
    if(cursor != null) 
     cursor.close(); 
} 
+0

你可以简单地通过这个例子来避免null和null检查。 – aij

+2

就像打开文件指针一样 - 总是在finally节中处理关闭以确保您的代码干净地存在。 – slott

11

我刚刚经历过这个问题 - 和建议不会关闭游标的答案,而有效的,没有我怎么固定它。我的问题是当SQLite试图重新填充它的游标时关闭数据库。我会打开数据库,查询数据库以获取游标到数据集,关闭数据库并遍历游标。我注意到每当我在该游标中遇到特定记录时,我的应用程序就会在OP中出现同样的错误。

我假设游标访问某些记录,它需要重新查询数据库,如果它关闭,它会抛出这个错误。我解决了这个问题,直到我完成了我所需要的所有工作。

62

如果您不得不深入研究大量的SQL代码,您可以通过将以下代码片段放入MainActivity以启用StrictMode来加快调试速度。如果检测到泄漏的数据库对象,那么您的应用程序现在会崩溃,日志信息会突出显示您的泄漏的确切位置。这帮助我在几分钟内找到一个流氓光标。

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    if (BuildConfig.DEBUG) {  
     StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() 
     .detectLeakedSqlLiteObjects() 
     .detectLeakedClosableObjects() 
     .penaltyLog() 
     .penaltyDeath() 
     .build()); 
    } 
    super.onCreate(savedInstanceState); 
    ... 
    ... 
+6

太棒了!我在我的主类的'static {...}'块中使用了'if(BuildConfig.DEBUG){...}'这个标准。 –

+1

调试迄今为止发现的那种问题的最好和最有效的方法应该是被接受的答案! – androidseb

+0

太棒了!但是在发布时会不使用StrictMode? – alfdev

0
public class CursorWindowFixer { 

    public static void fix() { 
    try { 
     Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize"); 
     field.setAccessible(true); 
     field.set(null, 102400 * 1024); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    } 
} 
+0

您应该添加一些注释来解释您的代码 – sme

+0

突破仅2兆字节的CursorWindow限制 –