I have an issue where my database handle is being closed during my test and I am not sure why.
The #Before method runs correctly, but the #Test method throws an exception : attempt to re-open an already-closed object. I am not closing it anywhere in my code, so I am not sure how it is getting closed.
The method marked with #Test is the only one in the test so far, so no other tests are running before it.
Can anyone help me understand what is happening here?
Test
#RunWith(AndroidJUnit4.class)
public class DatabaseTest {
private Database subject;
private SQLiteDatabase wDB;
public DatabaseTest(){
DbHelper helper = new DbHelper(InstrumentationRegistry.getTargetContext());
subject = new Database(helper);
wDB = helper.getWritableDatabase();
}
// remove any preexisting records from the database
#Before
public void cleanSlate(){
wDB.delete(Database.ITEMS_TABLE, null, null);
}
#Test
public void testInsert(){
Cursor c;
Item i = getMockItem();
subject.update(i);
// (per below) DatabaseTest.java:46:
c = wDB.query(Database.ITEM_TABLE,Database.ALL_COLUMNS,null,null,null,null,null);
//...
Error
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/user/0/com.example.app.debug/databases/storage.db
at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1314)
at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1257)
at com.example.app.data.local.DatabaseTest.testInsert(DatabaseTest.java:46)
Update Method
public void update(Item item){
SQLiteDatabase wDB = helper.getWritableDatabase();
ContentValues cv = entryToContentValues(item);
wDB.insertWithOnConflict(ITEM_TABLE, null, cv, SQLiteDatabase.CONFLICT_REPLACE );
wDB.close();
}
The issue is that you are closing the database in the update method and thus the database is closed when trying to run the query at line 46.
One solution would be to not close the database in the update method. However you should then close it when finished with the database.
Another solution could be to call getWriteableDatabase(); before the query but after the update.
Other solutions could range up to having a singleton thus ensuring that you only have a single instance/connection and then only closing the database when the App is destroyed (I'm using this approach in the app I am using).
P.S. you should also close cursors then they are finished with as they can cause Too many Open errors/issues. Personally I tend to put cursor closing into the activities onDestroy method.
Related
I am having a big problem with my database. When I try to make the first call to the database since the app launches, I get a crash saying:
attempt to re-open an already-closed object: SQLiteDatabase /data/user/0/<path_to_db>
I am using a DBHelper class which extends SQLiteOpenHelper.
When I launch the app the first time, it should create the DB etc, and then have an empty DB which should still allow me to do some queries on it.
However, when I do my first query, it crashes with the above error.
Here's the query:
public TerminalList getTerminalLocations() {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(LOCATION_TABLE_NAME, null, null, null, null, null, null);
//do stuff
cursor.close();
db.close();
return locationList;
}
Its crashing on the first line!!
SQLiteDatabase db = getReadableDatabase();
It doesn't even get as far as the cursor!
How is this possible? Does anyone know how to fix this?
Does it make a difference that this is being called from the OnCreateView of my fragment? Here is how I call it. Its pretty standard:
private void getLocations() {
DBHelper db = DBHelper.getInstance(getActivity());
TerminalList locations = db.getTerminalLocations();
//do stuff
}
I've tried commenting out this call, but it pushes the problem to the next DB call, so is happening with every single DB call in the app!!
Thanks.
Try something like this for the utility method:
public Cursor getWhatyouNeed(SQLiteDatabase db, params...) {
Cursor yourData = db.query(your_query_params...);
// your logic
return yourData;
}
In my app I want to initialize some data stored in a SQLite database.
But it turns out the SQLiteOpenHelper onCreate method is not called when I read my database (it is when I write into my database).
My app crash when I read the db for a table that does not exist (it does not crash if I create the table before). I could catch the exception but does not seem very clean to me.
Is it the normal behavior regarding SQLiteOpenHelper onCreate method or am I missing something?
Here is the initialisation function called in activity OnCreate()
private void InitializeDbPlayerList() {
SQLiteDatabase db;
Cursor cursorPlayers;
DbPlayerData dbPlayerData;
db = mGameDbHelper.getReadableDatabase ();
// The following line crash the app if PLAYER_TABLE_NAME does not exist
cursorPlayers=db.query(GameDatabaseOpenHelper.PLAYER_TABLE_NAME,
GameDatabaseOpenHelper.player_columns, null, new String[] {}, null, null,
null);
cursorPlayers.moveToFirst();
for(int j=0;j<cursorPlayers.getCount();j++)
{
dbPlayerData = new DbPlayerData(cursorPlayers.getString(0),cursorPlayers.getFloat(1),cursorPlayers.getFloat(2),
cursorPlayers.getInt(3),cursorPlayers.getInt(4));
mDbPlayerList .add(dbPlayerData);
}
}
Thx
Fabien
I have an app that functions properly and does not force close or crash. But when I look at LogCat, it occasionally gives me this:
05-20 15:24:55.338: E/SQLiteDatabase(12707): close() was never explicitly called on database '/data/data/com.---.--/databases/debt.db'
05-20 15:24:55.338: E/SQLiteDatabase(12707): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
a little ways down...
05-20 15:24:55.338: E/System(12707): Uncaught exception thrown by finalizer
05-20 15:24:55.338: E/System(12707): java.lang.IllegalStateException: Don't have database lock!
I am not sure when I should be opening and closing my Database?
I have a Main activity that is simply a splash screen. It then goes into an activity that calls a ListView using info from the DB; so it is at this activity where the DB is first opened.
There is also one other Activity where the DB is required that branches off the one with the ListVeew. When am I supposed to be opening and closing this? Word seems to be that I simply need to open once, and then close when the app is "paused", "stopped" or "destroyed".
If this is the case, where do I put the db.close() method... in the Splash Screen Main Activity where onStop, etc is located? or the same Activity as the one that opens the DB? or.. is there another place?
UPDATE:
This is the line in code that the error keeps pointing to:
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
If you're using an instance of a DatabaseHelper class, and after you initialize the DBHelper object, every time you do work in the database you should call the open method before you do work, then create a new cursor, query the database, do work with the information you just stored in the cursor, when you're done close the cursor, then close the database. For example if you wanted to grab every item in a database you would do something like :
...
DataBaseHelper db = new DataBaseHelper(this);
...
db.open();
Cursor cursor = db.getAllItems();
maxCount = cursor.getCount();
Random gen = new Random();
row = gen.nextInt(maxCount); // Generate random between 0 and max
if (cursor.moveToPosition(row)) {
String myString = cursor.getString(1); //here I want the second column
displayString(myString); //private method
}
cursor.close();
db.close();
getAllItems is a public method in my DatabaseHelper, it looks like this in case you were wondering
public Cursor getAllItems() {
return db.query(DATABASE_TABLE,
new String[] {
KEY_ROWID,
KEY_NAME
},
null,
null,
null,
null,
null);
}
This is how I access my database and I haven't gotten any of the errors you've got, and it works perfectly.
I used to do the way #Shikima mentioned above but in complex applications which has many background services, multi-threading,etc it can get real tiresome when you have to manage many database instances and on top of that, opening and closing them.
To overcome this, I used the following method and it seems to be working fine.
1.
Declare and initialize an instance of YourDBHelperClass in your Application base class like this :
public class App extends Application {
public static YourDBHelperClass db;
#Override
public void onCreate() {
super.onCreate();
db = new YourDBHelperClass(getApplicationContext());
db.open();
}
}
2.
In you activity, or any other place you want to use the DB, initialize the YourDBHelperClass object like this :
YourDBHelperClass db = App.db;
And then you can use the database anyway you want without having to worry about opening and closing it manually each time. The SQLiteOpenHelper takes care of the closing when the Application is destroyed
You are probably not handling your database correctly; you are opening more database instances than you are closing.
There are a number of design patterns you can follow to correct this behavior. You might want to consult this answer for more information.
I'm getting two contradicting Exceptions when creating and populating my new SQLiteDatabase in Android. In short my code:
SQLiteOpenHelper extending class:
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_TABLE_CREATE);
loadLevelData(db); //puts data in the database
//db.close(); <<< ?
}
In my activity class I instantiate this class (in onCreate()), and call getWritableDatabase():
dbHelper = new DbOpenHelper(getApplicationContext());
database = dbHelper.getWritableDatabase();
Now if I don't call db.close() after populating the database, like above, I get
android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
However if I DO close it, I get the following exception:
java.lang.IllegalStateException: database not open
on getWritableDatabase().
This really confuses me, so could anyone help me with what's wrong?
You are not expected to close the database in the DatabaseHelper class. However you need to close it every time you open it calling getWritableDatabase:
dbHelper = new DbOpenHelper(getApplicationContext());
database = dbHelper.getWritableDatabase();
//... do something with database
database.close();
You are closing your database at the wrong time.
I typically keep the database around like this:
public class MyActivity extends Activity {
SQLiteDatabase writeableDb;
// ...
// Code
// ...
public void onStart(){
super.onCreate(savedState);
// Do stuff, get your helper, etc
writeableDb = helper.getWriteableDatabase();
}
public void onStop(){
writeableDb.close();
super.onStop();
}
}
Alternatively, wrap all your code working with that db connection in a try/finally block
db = helper.getWriteableDatabase();
try { // ... do stuff ... }
finally { db.close(); }
Note: All of the opening/closing should be done in the Activity working with the database, not the open helper.
i'm trying some db access code for the first time and i'm getting some errors and would
appreciate some help. this is what is happening:
1) i'm creating a new instance of SQLiteOpenHelper by calling this constructor:
public MyDbOpenHelper(Context context) {
super(context, "some_name.db", null, 1);
}
the above instance is the variable myHelper used below
2) i'm calling myHelper.getReadableDatabase()
3) at this point Android calls my onCreate method. my implementation of this method
begins as follows:
public void onCreate(SQLiteDatabase db) {
db.setLocale(new Locale("en"));
//...
}
4) the above call does not complete. i get the following dump:
BEGIN TRANSACTION failed setting locale
couldn't open some_name.db for writing (will try read-only):
android.database.sqlite.SQLiteException: cannot start a transaction within a transaction
at android.database.sqlite.SQLiteDatabase.native_setLocale(Native Method)
at android.database.sqlite.SQLiteDatabase.setLocale(SQLiteDatabase.java:1751)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper...
at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper....
it looks like since i'm trying to get a readable db when the db does not yet exist, so Android
tries to create it and creates a writable db for the setLocale... and is that why
it complains about "transaction within transaction"?
how is it supposed to be done?
thanks, any help would be appreciated