2012-07-05 19 views
54

SQLiteデータベースに対して毎秒何回も異なるクエリを実行するルーチンがあります。しばらくするとエラーが発生する2048 kbのSQLite Android Database Cursorウィンドウ割り当てに失敗しました

"android.database.CursorWindowAllocationException: - Cursor window allocation of 2048 kb failed. # Open Cursors = "がLogCatに表示されます。

私はアプリのログメモリ使用量がありましたが、実際には使用量が一定の限界に達したときにこのエラーが発生し、それがなくなったことを意味します。私の直感は、クエリを実行するたびにデータベースエンジンが新しいバッファ(CursorWindow)を作成していることを示しています。また、カーソルを.close()に設定してもガベージコレクタもSQLiteDatabase.releaseMemory()もメモリを解放するほど速くありません。私は、このソリューションは、データベースを常に同じバッファに書き込むように強制し、新しいバッファを作成するのではないと思っていますが、これを行う方法を見つけることができませんでした。私は自分自身のCursorWindowをインスタンス化しようとしましたが、SQLiteCursorを無駄に設定しようとしました。

¿任意のアイデアですか?

EDIT:@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ライブラリと関連があります。また、カーソルを閉じることもできますが、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セクションで常にcloseを処理します。 – 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

Breakthrough CursorWindowの制限はわずか2 MBです –

関連する問題