Se você estiver recebendo uma mensagem indicando que há muitos arquivos abertos, uma causa pode ser que há muitos cursores ainda abertos.
No entanto, a mensagem retornada pode não ser sempre a mesma e provavelmente é específica para a tarefa/chamada que está sendo chamada.
Nesse caso, a mensagem era
(unable to open database file (code 2062))
, ainda em outro caso (de um SELECT a mensagem foi unable to open database file (code 14)
). SQLite incapaz de abrir o arquivo de banco de dados (código 14) na consulta “SELECT” frequente. O link acima também aponta para um post que fiz o que mostra claramente que a criação de um cursor resulta em um arquivo (ou arquivos) sendo aberto.
O exemplo estava percorrendo cerca de 500 linhas e para cada linha estava criando/recriando 3 cursores para cada linha (portanto, potencialmente mais de 1500 cursores, embora usando apenas 4 objetos de cursor).
Inicialmente, estava apenas fechando os 3 cursores no final (última linha do pai de todos) resultando no
unable to open database File (code 14)
. Fechar os 3 cursores para cada iteração resolveu o problema. O código que falhou foi:-
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();
}
Enquanto o código fixo foi:-
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();
}
Eu costumo seguir agora a seguinte regra/prática:-
-
Se apenas obter o resultado, por exemplo. obtendo o número de linhas, feche o Cursor no método.
-
Se estiver usando o Cursor para uma exibição, por exemplo, um ListView e, em seguida, feche o cursor noonDestroy
da atividade método.
-
Se estiver usando o Cursor para o que chamarei de processamento mais complexo, por exemplo. excluir linhas com referências subjacentes e fechar os cursores assim que terminar, dentro do(s) loop(s) de processamento.