Android: database issue with ContentProvider - java.lang.IllegalStateException - android

in my case, I use ContentProvider together with CursorAdapter, the content provider has the methods to query, and bulkInsert. if the bulkInsert runs before query method, I get the exception:
java.lang.IllegalStateException: database /data/data/com.example/databases/testdb (conn#0) already closed
I think this is because in the bulkInsert method:
public int bulkInsert(Uri uri, ContentValues[] insertValuesArray) {
switch (sUriMatcher.match(uri)) {
case ROUTE_ENTRIES:
SQLiteDatabase localSQLiteDatabase = mDatabaseHelper.getWritableDatabase();
localSQLiteDatabase.beginTransaction();
localSQLiteDatabase.setTransactionSuccessful();
// do the work
// Ends the transaction and closes the current db instances
localSQLiteDatabase.endTransaction();
localSQLiteDatabase.close();
}
}
as you can see, localSQLiteDatabase.close() is called in the bulkInsert method. I did this as I followed the threadsample from google. Link: https://code.google.com/p/android-source-browsing/source/browse/samples/training/threadsample/src/com/example/android/threadsample/DataProvider.java?repo=platform--development&name=tools_r22&r=2c063c889aa816e0de91bf17fdc0c78f48d5e2d0
can anyone suggest if I should close the database in the bulkInsert or not? if I don't close, is there any leak?
thanks a lot!
cheers,
Martin

Related

Closing the SQLiteDatabase in a Content Provider

I'm looking through the NotePad example that comes with the Android SDK and I was wondering if someone could clarify why the the variable db is never closed in the update function? It's usually a good idea to close the database when it is no longer in use to prevent leaks. Ideas?
#Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
// Opens the database object in "write" mode.
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
... // More code, db.close() is never called?!
// Returns the number of rows updated.
return count;
}

How to Open/Close SQLite db in Android Properly

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.

Refresh/Reload database reference in custom ContentProvider after restore

I use a ContentProvider in my app and everything works great except for one little issue. I have a backup and restore function that backs up the database to a file on the SD card and then those backup files can be restored to overwrite the current database. This whole process is working, but the ContentProvider still holds the reference/cache to the original database once one of the old backup files is restored. I can't seem to find a way to refresh or reload the database reference in the ContentProvider. I know the restore works because I can see the records in the db with SQLite Editor and when I close and re-open the app, it displays the correct records.
Does anybody know a way to do this? Is there a way to close and re-open the ContentProvider that I'm not seeing?
If you are targeting >= API 5 you can get a reference to your ContentProvider via a ContentProviderClient, and run a method specific to your implementation:
ContentResolver resolver = context.getContentResolver();
ContentProviderClient client = resolver.acquireContentProviderClient("myAuthority");
MyContentProvider provider = (MyContentProvider) client.getLocalContentProvider();
provider.resetDatabase();
client.release();
Add the reset method to your ContentProvider implementation:
public void resetDatabase() {
mDatabaseHelper.close();
mDatabaseHelper = new MyDatabaseOpenHelper(context);
}
Are you maintaining a reference to the actual SQLiteDatabase in your content provider (something like calling SQLiteOpenHelper.getWritableDatabase() in onCreate() and then keeping that reference)? Or do you get the DB object from someplace like a helper in each provider method?
Typically, if you only keep a local reference to the helper and get the writable/readable database instance inside of each method as needed then this problem should go away. If not, perhaps we can take a look at the provider code?
Hope that Helps!
Here is my solution.
public class DataProvider extends ContentProvider {
private DataDbHelper dbHelper;
#Override
public boolean onCreate() {
// nothing here
return true;
}
private DataDbHelper getDbHelper() {
if (dbHelper== null) {
// initialize
dbHelper = new DataDbHelper(getContext());
} else if (dbHelper.getReadableDatabase().getVersion() != DataDbHelper.VERSION) {
// reset
dbHelper.close();
dbHelper = new DataDbHelper(getContext());
}
return this.mOpenHelper;
}
}
query(), insert(), update(), delete() use getDbHelper() to obtain an SQLiteDatabase
The full code of my Android app is available here if you need more info.
You can also simply use the delete method without a selection:
context.getContentResolver().delete(YourProvider.CONTENT_URI, null, null);

Android Cursor with ORMLite to use in CursorAdapter

Is there any way to get Cursor for a query, which I am processing with ORMLite Dao object?
ORMLite now supports next(), previous(), moveRelative(offset), ... methods on the CloseableIterator class. This should allow you to move the underlying Cursor object around at will.
It also supports the following DAO Cursor methods:
dao.mapSelectStarRow(databaseResults) Return the latest row from the database results from a query to select *. With this you can change the cursor location (for example) and then get the current object.
dao.getSelectStarRowMapper() Provides a mapper that you can use to map the object outside of the Dao.
When you are building your own query with ORMLite, you use the QueryBuilder object. queryBuilder.prepare() returns a PreparedQuery which is used by various methods in the DAO. You can call dao.iterator(preparedQuery) which will return a CloseableIterator which is used to iterate through the results. There is a iterator.getRawResults() to get access to the DatabaseResults class. Under Android, this can be cast to an AndroidDatabaseResults which has a getCursor() method on it to return the Android Cursor.
Something like the following code:
// build your query
QueryBuilder<Foo, String> qb = fooDao.queryBuilder();
qb.where()...;
// when you are done, prepare your query and build an iterator
CloseableIterator<Foo> iterator = dao.iterator(qb.prepare());
try {
// get the raw results which can be cast under Android
AndroidDatabaseResults results =
(AndroidDatabaseResults)iterator.getRawResults();
Cursor cursor = results.getRawCursor();
...
} finally {
iterator.closeQuietly();
}
This is a bit complicated but you are definitely having to peer behind the vale to get to this object which is hidden by the database abstraction classes.
Did you try some of Gray's advice from this post? He explains how you can select a column as another name, such as, select id as _id.
If you're in an Activity and don't want to mess around with the QueryBuilder give the following a go, which is just as effective.
Cursor cursor = getHelper().getReadableDatabase().query(tableName, projection, selection, selectionArgs, groupBy, having, sortOrder)
If you mean the getHelper() method to reach the dao methods create etc. you only have to inherit from the OrmLiteBaseActivity<YourDBHelper> and you can call it. It will look sth like this:
public class YourClass extends OrmLiteBaseActivity<YourDBHelper> {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
getHelper().getDao().queryForAll();
...
}
}
If you mean the cursor to handle database operation: I don't think that you can reach it! But I don't understand why you should need it. ORMLite has nearly all functions of the cursor. So what do you need it for?

Android SQLite: Database empty in second activity

I've searched all over the net for a solution to this so lets hope someone here can help.
My app has a start up task which populates a SQLite database before loading the main menu. A second activity that can be accessed from the main menu needs access to this. Therefore I close the database in the first activity in order to stop locking errors.
For some reason, the database seems to have no rows as soon as I close the connection, in both the same activity and the second activity.
Heres a code sample:
SQLiteDatabase db = getWritableDatabase(); // get instance of current database
db.beginTransaction(); // set exclusive mode to speed up
for(GulbArticle g : gulbArticles){
this.insert(g);
}
db.setTransactionSuccessful();
// counting here returns 315 rows using the all2() function below
db.close();
// counting here returns 0 rows using the all2() function below
Here is a function I made to get the count back
public void all2(){
SQLiteDatabase db = getReadableDatabase();
String sql = "SELECT COUNT(*) FROM "+TABLE_NAME;
SQLiteStatement statement =db.compileStatement(sql);
long count = statement.simpleQueryForLong();
Log.v("ccount2",count+"");
}
So in both cases I'm initializing an instance of the database, but for some reason as soon as I close it once I can't reopen it/there seems to be nothing in the database. Maybe I'm missing something simple but this has really stumped me.
It seems you forgot to call db.endTransaction();, i.e. to commit the transaction.
It should read:
db.setTransactionSuccessful();
db.endTransaction();
db.close();
Also it is good idea to surround it in try-catch-finaly like this:
try {
db.beginTransaction();
// do your DB manipulation
db.setTransactionSuccessful();
} catch(...) {
...
} finally {
db.endTransaction();
}
db.close();
You don't need to close database when you start second activity. SQLite is normal DB management system - which means that these kind of situation should be resolved.
Resolving of this usually done by transactions mechanism - which you're tried to use in your code.
In your case I believe you have to use non-eclusive transactions - i.e. when record is locked in exclusive mode - record in unaccessible, so basic idiom should looks like:
db.beginTransactionNonExclusive();
try
{
// do smth
db.setTransactionSuccessful();
}
finally
{
db.endTransaction();
}

Categories

Resources