78

SQLite データベースに対してさまざまなクエリを毎秒何度も実行するルーチンがあります。しばらくすると、エラーが発生します

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

アプリでメモリ使用量をログに記録しましたが、実際に使用量が特定の制限に達すると、このエラーが発生し、不足していることを意味します。私の直感では、クエリを実行するたびにデータベース エンジンが新しいバッファ (CursorWindow) を作成していることがわかります。カーソルを .close() しても、ガベージ コレクタもSQLiteDatabase.releaseMemory()メモリを解放する速度も十分ではありません。解決策は、データベースが常に同じバッファーに書き込み、新しいバッファーを作成しないように「強制」することにあると思いますが、これを行う方法を見つけることができませんでした。私は自分の 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ます。

4

8 に答える 8

111

ほとんどの場合、このエラーの原因は閉じていないカーソルです。カーソルを使用した後は、必ずすべてのカーソルを閉じてください(エラーが発生した場合でも)。

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();
}

カーソルを閉じていないときにアプリをクラッシュさせるには、アプリケーションで厳密モードをdetectLeakedSqlLiteObjects有効にしますonCreate

StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
   .detectLeakedClosableObjects()
   .detectLeakedSqlLiteObjects()
   .penaltyDeath()
   .penaltyLog()
   .build();
StrictMode.setVmPolicy(policy);

明らかに、これはデバッグビルドに対してのみ有効にします。

于 2013-03-20T22:43:04.773 に答える
6

実際、Android SQLite カーソル ウィンドウが使用できる最大サイズは 2 MB であり、このサイズを超えると上記のエラーが発生します。ほとんどの場合、このエラーは、SQL データベースに BLOB として保存されている大きなイメージ バイト配列または長すぎる文字列が原因で発生します。これが私がそれを修正した方法です。

たとえば、Javaクラスを作成します。FixCursorWindow に以下のコードを挿入します。

    public static void fix() {
        try {
            Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize");
            field.setAccessible(true);
            field.set(null, 102400 * 1024); //the 102400 is the new size added
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

次に、アプリケーション クラスに移動し (まだ作成していない場合は作成します)、次のように FixCursorWindow を呼び出します。

public class App extends Application {

public void onCreate()
{
    super.onCreate();
    CursorWindowFixer.fix();

}

}

最後に、このようにアプリケーション タグのマニフェストにアプリケーション クラスを含めるようにしてください。

    android:name=".App">

これですべてです。これで完全に機能するはずです。

于 2018-06-06T17:11:33.520 に答える
-1

これは、特に外部 SQLite を使用している場合の通常の例外です。次のように Cursor オブジェクトを閉じることで解決できます。

if(myCursor != null)
        myCursor.close();

つまり、カーソルにメモリがあり、開いてから閉じると、アプリケーションが高速になり、すべてのメソッドが必要とするスペースが少なくなり、データベースに関連する機能も改善されます。

于 2018-09-18T07:06:57.533 に答える
-3
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();
    }
  }
}
于 2018-03-09T02:00:57.197 に答える