Jeśli otrzymujesz komunikat wskazujący, że otwartych jest zbyt wiele plików, przyczyną może być to, że jest zbyt wiele otwartych kursorów.
Jednak zwrócona wiadomość może nie zawsze być taka sama i prawdopodobnie jest specyficzna dla wywoływanego zadania/połączenia.
W tym przypadku komunikat to (unable to open database file (code 2062))
, jeszcze w innym przypadku (z SELECT wiadomość była unable to open database file (code 14)
). SQLite nie może otworzyć pliku bazy danych (kod 14) przy częstym zapytaniu „SELECT”.
Powyższy link wskazuje również na post, który napisałem, co dość wyraźnie pokazuje, że utworzenie kursora skutkuje otwarciem pliku (lub plików).
Przykładem było przechodzenie przez około 500 wierszy i dla każdego wiersza tworzył/odtwarzał 3 kursory dla każdego wiersza (więc potencjalnie 1500+ kursorów, mimo że używano tylko 4 obiektów kursora).
Początkowo zamykało tylko 3 kursory na końcu (ostatni wiersz rodzica wszystkich), co spowodowało unable to open database File (code 14)
. Zamknięcie 3 kursorów dla każdej iteracji rozwiązało problem.
Kod, który się nie powiódł, to :-
SQLiteDatabase db = getWritableDatabase();
Cursor shoplistcursor = getAllRowsFromTable(SHOPLIST_TABLE_NAME);
Cursor productcsr;
Cursor aislecsr;
Cursor prdusecsr;
while(shoplistcursor.moveToNext()) {
productcsr = getProductFromProductId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
aislecsr = getAisleFromAisleId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)));
prdusecsr = getProductUsage(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)),
shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
if (productcsr.getCount() < 1 | aislecsr.getCount() < 1 | prdusecsr.getCount() < 1) {
deleteShopListEntry(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_ID)));
}
if(shoplistcursor.isLast()) {
prdusecsr.close();
aislecsr.close();
productcsr.close();
}
}
shoplistcursor.close();
db.close();
}
Podczas gdy stały kod to :-
SQLiteDatabase db = getWritableDatabase();
Cursor shoplistcursor = getAllRowsFromTable(SHOPLIST_TABLE_NAME);
Cursor productcsr;
Cursor aislecsr;
Cursor prdusecsr;
while(shoplistcursor.moveToNext()) {
productcsr = getProductFromProductId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
aislecsr = getAisleFromAisleId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)));
prdusecsr = getProductUsage(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)),
shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
if (productcsr.getCount() < 1 | aislecsr.getCount() < 1 | prdusecsr.getCount() < 1) {
productcsr.close();
aislecsr.close();
prdusecsr.close();
deleteShopListEntry(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_ID)));
} else {
productcsr.close();
aislecsr.close();
prdusecsr.close();
}
}
shoplistcursor.close();
db.close();
}
Mam tendencję do przestrzegania następującej zasady/praktyki:-
-
Jeśli po prostu otrzymujesz wynik, np. pobierając liczbę wierszy, zamknij Kursor w metodzie.
-
Jeśli używasz Kursora do wyświetlania np. ListView, a następnie zamknij kursor w
onDestroy
aktywności metoda. -
Jeśli używasz Kursora do czegoś, co nazywam bardziej złożonym przetwarzaniem, np. usuwanie wierszy z bazowymi referencjami, a następnie zamykanie kursorów, gdy tylko skończą, w ramach pętli przetwarzania.