2012-07-05 9 views
54

Mam rutynę, która uruchamia różne zapytania względem bazy danych SQLite wiele razy na sekundę. Po chwili bym się błądSQLite Android Database Przydzielenie okna kursora z 2048 kb nie powiodło się

"android.database.CursorWindowAllocationException: - Cursor window allocation of 2048 kb failed. # Open Cursors = " pojawić w LogCat.

miałem zużycie pamięci aplikacji dziennika, a nawet gdy użycie osiągnie określony limit, otrzymuję ten błąd, sugerując, że zabraknie. Moja intuicja podpowiada mi, że silnik bazy danych tworzy nowy bufor (CursorWindow) za każdym razem, gdy uruchamiam zapytanie, a mimo tego, że zaznaczam kursory .close(), ani moduł do zbierania śmieci, ani SQLiteDatabase.releaseMemory() nie są wystarczająco szybkie do uwolnienia pamięci. Myślę, że rozwiązaniem może być "wymuszenie" bazy danych, aby zawsze zapisywać w tym samym buforze, a nie tworzyć nowych, ale nie byłem w stanie znaleźć sposobu na zrobienie tego. Próbowałem tworzyć własne CursorWindow i próbowałem ustawić to i SQLiteCursor bezskutecznie.

¿Wszelkie pomysły?

EDIT: re przykład kodu wniosek @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); 
} 

Idealnie chciałbym móc .setWindow() przed wydaniem nowej kwerendy i mieć dane wprowadzone do tego samego CursorWindow Everytime I Poznawanie danych .

+0

nie wiem jaki jest problem .. :) ale używam aby SQLiteOpenHelper klasy singleton. Więc nigdy nie znalazłem żadnych takich problemów. –

+0

Nie używam SQLiteOpenHelper, tworzę statyczną klasę DataAccess, która zawiera bazę SQLiteDatabase. Działa to dobrze i wątpię, że problem istnieje. Problem polega bardziej na tym, że biblioteki SQLite tworzą NOWY kontener, w którym umieszcza się wyniki każdego nowego zapytania, zamiast ciągle używać tego samego kontenera. I chociaż mogę zamknąć kursor, szybkość, z jaką GC oczyszcza jest wolniejsza niż szybkość, z jaką tworzone są nowe pojemniki, tworząc w ten sposób pamięć pamięci. – alex

+0

Czy możesz pokazać niektóre z kodu, w szczególności, co próbujesz zrobić z ustawieniem własnego 'CursorWindow'? –

Odpowiedz

88

Najczęściej przyczyną tego błędu są niezamknięte kursory. Upewnij się, że zamknąłeś wszystkie kursory po ich użyciu (nawet w przypadku błędu).

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

W rzeczywistości możesz po prostu tego przykładu, aby uniknąć kontroli zerowej i zerowej. – aij

+2

Podobnie jak otwarte wskaźniki plików - ZAWSZE zajmuj się zamknięciem w sekcji końcowej, aby upewnić się, że twój kod istnieje w sposób czysty. – slott

11

właśnie doświadczył tego problemu - a sugerowana odpowiedź nie zamykając kursor natomiast ważne, nie było jak naprawiłem go. Mój problem polegał na zamykaniu bazy danych, gdy SQLite próbował ponownie zaludnić kursor. Otworzyłem bazę danych, wysłałem zapytanie do bazy danych, aby uzyskać kursor do zestawu danych, zamknąć bazę danych i powtórzyć kursor. Zauważyłem, że za każdym razem, gdy uderzyłem w pewien kurs w tym kursorze, moja aplikacja zawiesiłaby się z tym samym błędem w OP.

Zakładam, że kursor dostęp do niektórych zapisów, musi ponownie zapytanie do bazy danych, a jeśli jest ona zamknięta, będzie rzucać ten błąd. Naprawiłem to, nie zamykając bazy danych, dopóki nie ukończyłem całej pracy, której potrzebowałem.

62

Jeśli musisz przekopać znaczną ilość kodu SQL, możesz przyspieszyć debugowanie, umieszczając poniższy fragment kodu w MainActivity, aby włączyć StrictMode. Jeśli wykryte zostaną wyciekające obiekty bazy danych, aplikacja będzie teraz ulegać awarii z informacjami o logach podświetlającymi dokładnie miejsce, w którym nastąpił wyciek. Pomogło mi to zlokalizować nieuczciwy kursor w ciągu kilku minut.

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

To świetnie! Stworzyłem ten standard za pomocą 'if (BuildConfig.DEBUG) {...}' wewnątrz bloku 'static {...}' mojej głównej klasy. –

+1

Najlepszym i najskuteczniejszym sposobem debugowania tego rodzaju problemu, który znalazłem do tej pory, powinna być zaakceptowana odpowiedź! – androidseb

+0

Świetnie! Ale w wersji będzie działać bez 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

Powinieneś dodać kilka komentarzy wyjaśniających kod – sme

+0

Limit przełomowego CursorWindow wynoszący tylko 2 megabajty –

Powiązane problemy