2012-07-05 75 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限制 –