Android SQlite multithread access - android

I am using SQLiteOpenHelper to write and read from SQlite database on Android. When user clicks on UI I read from SQLite database using AsyncTask but at the exact samo moment I am updating and writing to the database in the background using other AsyncTask.
Every x times I get database locked exception. How can I fix this? Can SQlite be accessed somehow from multiple thread at the same time?
I am using it like that: I have a Database class which extends from SQLiteOpenHelper. I implemented onCreate and onUpgrade methods and everytime I am reading from database or writing to database I use SQLiteDatabase like that:
SQLiteDatabase database = null;
try {
database = new Database(context).getWritableDatabase();
....
writing and reading from database...
....
} catch (Exception e) {
e.printStackTrace();
} finally {
if (database != null) {
database.close();
}
}
At the end I also close SQLiteStatements and Cursors if I use them. Should I use #Justin answer and have a singleton of database class in Application?
This is the error I get:
E/SqliteDatabaseCpp(17377): sqlite3_open_v2("/data/data/com.skulptur/databases/LGM.s3db", &handle, 6, NULL) failed
E/SQLiteDatabase(17377): Failed to open the database. closing it.
E/SQLiteDatabase(17377): android.database.sqlite.SQLiteDatabaseLockedException: database is locked
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteDatabase.dbopen(Native Method)
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:983)
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:956)
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1021)
E/SQLiteDatabase(17377): at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:750)
E/SQLiteDatabase(17377): at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221)
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:149)
E/SQLiteDatabase(17377): at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:223)

This is what I do to avoid locking issues.. I let my application handle a single instance of my db helper and always refer to it to get a handle on my DB.
public class MyApp extends Application {
// My instance of SQLiteOpenHelper
public static DbHelper openHelper;
#Override
public void onCreate() {
super.onCreate();
if (openHelper == null) {
openHelper = new DbHelper(this);
}
}
public synchronized static SQLiteDatabase getDB() {
return openHelper.getWritableDatabase();
}
}
Once you do this, anytime you want to do a db query, get the db handle like MyApp.getDB() and you will never have a locking issue. Be sure to NEVER close the DB though. This pattern maintains a single connection at all times in your app. SQLite is meant to be synchronous on Android so if you have long running DB processes, like a 1000 inserts in a single transaction you'll want to code it like this:
db.beginTransaction();
try {
for (DBRow row : insertList) {
// your insert code
insertRow(row);
db.yieldIfContendedSafely();
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
This will allow other queries to interject themselves so that you won't see your app grind to a halt during background updates.

Implement a ContentProvider on top of the Database with Loaders it will handle the threading.
You can consult this tutorial: http://www.vogella.com/articles/AndroidSQLite/article.html

There are few things to try.
First try passing the same instance of SQLiteOpenHelper to every of your tasks.
Second is using beginTransaction and endTransaction on your SQLiteDatabase inside of your Helper.
Third is adding synchronized to every of your helper methonds being used in tasks.

I think you are using database connections incorrectly. It can be caused by unclosed cursors or smth else. Sqlite database implementation is thread safe and can be accessed simultaneousely from different threads.
Please provide more details about your problem (exception stack trace, code sample that causes exception etc) in order we can help you.

Related

What is the best practice when closing SQLiteDatabase in Android?

In official docs, you read:
public SQLiteDatabase getWritableDatabase ()
...
Once opened successfully, the database is cached, so you can call this method every time you need to write to the database. (Make sure to call close() when you no longer need the database.)
As for the SQLiteOpenHelper, I implemented a singleton pattern for the only instance, but how about the SQLiteDatabase?
How effective is the cache?
Android SQLite DB When to Close
I looked at this post, but there is no discussion over caching.
Open and close your db before and after each operation, respectively.
Never leave it open, when not needed.
Your mantra should be: open it, use it, close it.
Repeat for every db operation.
Same goes for Cursors as well.
I always close my SQLiteDatabase after every query. For example, here's how I implement my insert methods:
public void insert(ContentValues valuesMap){
SQLiteDatabase db = getWriteableDatabase();
try{
db.insert("ContactsTable", null, valuesMap);
}catch(Exception e){
// do someting...
}finally{
db.close()
}
}

Async tasks and Sqlite

I have a service that runs when device is connected to the internet. It fetches data from sqlite which is then uploaded on the server. A listfragment has been populated with the data from the database.
Since I have service, I am getting errors like "database already closed" "database not opened".
My dbHelper object is static.
I am calling SQLiteDatabase db = this.getWritableDatabase(); at the start of each method that will acess the database. And db.close() at the end of each method.
Should I close the db.close() only in onDestroy()? ... Would this solve the issue or is there another way?
first of all, if you ever want to close a database, you have to check if it is isOpen().
Now, in general your SQLiteOpenHelper implementation sould be a singleton and override the close() method to be:
#Override
public synchronized void close() {
mOpenConnections--;
if (mOpenConnections == 0) {
super.close();
}
}
where mOpenConnections is a member that holds the number of connections made through the SQLiteOpenHelper. this member should be incremented everytime somebody opens the database.
#Override
public synchronized void onOpen(SQLiteDatabase db) {
super.onOpen(db);
// increment the number of users of the database connection.
mOpenConnections++;
}
then when you need to close a database use SQLiteOpenHelper.close() method.
This should solve all your problems.
The only solution that I have found to work reliably across multiple activities is to call getWritableDatabase in the onCreate method of every activity.
As has been pointed out, any service should have its own SQLiteOpenHelper instance. I think this happens anyway, because the service runs in a separate address space, but it is best to make sure.

sqlite access from multiple threads for reading

there are similar questions but not clear answer around using sqlite db from multiple threads.
Consider the following scenario:
class MainActivity extends Activity {
DbHelper db; //extends sqliteopenhelper
...
void M1() {
db.getReadableDatabase();
Cursor c = db.query("...."
...
db.close();
}
void M1() {
db.getReadableDatabase();
Cursor c = db.query("...."
...
db.close();
}
ok, this is not multi-thread but question is that,
does it become a problem to use the same instance of sqliteopenhelper instance (i.e. db) like above, opening and closing in many times in different methods?
And my scenario is the following:
myAsync extends AsyncTask
doInBackground(.. {
do something using M1(); //this is a background thread
}}
onResume()...{
myAsync.execute();
M2(); //this is the main thread
...
}
if you see that in the async scenario, it is probably that two method can access the same database at the same time(for reading only- how about writing?). But they are using the same instance of SqliteOpenHelper. Would it cause to collision and if so how to avoid this?
I would like to learn more about sqlite database and concurrency
Any ideas would be greatfully appreciated.
ya there is a chance of not collision but your db may close when another thread is aquired, you can go for "Synchronized" concept of thread ie priority and etc when we use synchronized keyword then, one thread completes it task then other thread will starts

Renaming an SQL database in android

When referring to database tables I usually use syntax like this: my_database_name.my_table_name
I am trying to do the same in Android but am having trouble understanding how to name a database.
Would you just execute this SQL as you would in the Sqlite3 client? i.e.
ATTACH "my_database_file" AS my_database_name;
Here is what I tried in my onCreate method:
db.execSQL("Attach 'hq_db' AS hq_db;");
but I'm getting this error:
04-05 11:13:35.676: ERROR/AndroidRuntime(860): Caused by: android.database.sqlite.SQLiteException: cannot ATTACH database within transaction: Attach 'hq_db' AS hq_db;
How do I execute SQL statements on Android outside of a transaction to make this work?
Edit: It might also have something to so with the superclass constuctor, though the super class constructor string sets the file name (which is working properly) and it seems nothing else:
private class databaseOpenHelper extends SQLiteOpenHelper {
public q_player_databaseOpenHelper() {
super(MyApp.getContext(), "my_db_file", null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("Attach 'my_db_file' AS my_db_name;");
db.execSQL("PRAGMA foreign_keys=ON;");
}
Note: I have stripped this code down so it would fit my problem
You need to call db.execSQL("ATTACH 'hq_db' AS hq_db;"); before you start any kind of transaction. Do the attachment first, and then start the transaction.
Are you calling beginTransaction or executing SQL which begins a transaction before trying to attach your other database?
You're probably creating the database with AutoCommit=0. This puts you in a transaction so that it doesn't need to write to the database file after every INSERT. But it also means you can't ATTACH to another database. Create the DB, set AutoCommit=1, ATTACH, then set AutoCommit=0.

How careful should I be with thread-safety when creating methods/activities which interact with SQLite database?

I am creating an app which allows for many different Activities to be started from a TabActivity(up to ~25). Most of the activities require data from the sqlite database, so when onCreate is run, an AsyncTask creates an SQLiteOpenHelper object(which will open a readable/writable database), runs a query, data is retrieved, and everything is then closed.
i was just testing messing around to see if i could break something, so i added every Activityto the TabActivity's TabHost. I then started mashing each tab as quickly as possible.
i noticed that very quickly i began to see in the LogCat: Caused by: android.database.sqlite.SQLiteException: database is locked: BEGIN EXCLUSIVE; and the app proceeded to die.
Typically there will only be about 4-6 tabs(i can just limit the user anyway) for the TabHost. I haven't been able to break anything with a small amount of tabs to mash, but i am still worried that maybe i am accessing the database in a poor way.
How can i prevent my SQLiteDatabase objects to cause a lock?
If i create a ContentProvider will that eliminate the possibility of database locking?
Do you have any suggestions for changes I could make for accessing data from an SQLiteDatabase?
I ended up taking the approach of using the Application class and storing 1 SQLiteOpenHelper and trying my best to keep it synchronized. This seems to be working great - i put all my 25 activities in the TabHost and mashed away on them with no errors.
I am calling ((SQLiteDbApplication)getApplication()).setDbHelper(new DBHelper(this, Constants.DB_NAME, null, Constants.DB_VERSION_CODE)); method(shown below) in every onCreate() in my activities
Any further suggestions to this approach or to the changes i made using this Application class?
import android.app.Application;
import android.database.sqlite.SQLiteDatabase;
public class SQLiteDbApplication extends Application {
private DBHelper dbHelper;
private SQLiteDatabase db;
public synchronized DBHelper getDbHelper() {
db = dbHelper.getDatabase();//returns the already opened database object
while(db.isDbLockedByCurrentThread() || db.isDbLockedByOtherThreads());
return dbHelper;
}
public synchronized void closeDb() {
if(null != dbHelper)
dbHelper.close();
if(null != db)
db.close();
}
#Override
protected void finalize() throws Throwable {
if(null != dbHelper)
dbHelper.close();
if(null != db)
db.close();
super.finalize();
}
public synchronized void setDbHelper(DBHelper dbHelper) {
if(null == this.dbHelper) {
this.dbHelper = dbHelper;
this.dbHelper.setDb(this.dbHelper.getWritableDatabase());//creates and sets the database object via getWritableDatabase()
}
}
}
If you are to worried about all the database connections try to limit yourself to one SqliteOpenHelper and be sure to wrap a synchronization layer around it.
You can extend the application class and then call getApplication and cast the object you get into your application. Now you can store a SqliteOpenHelper in this application class and build your own thread safe access method to the database connection.
If you are using AsyncTask in all of your onCreate methods and you are experiencing problems with a lot of tabs these problems can also occur with a slower device, a faster user or a database that is grown big over the time of usage.
Depending on the use case of you app you can go the save way and go through all the effort and pain of threading and locking, or you can just publish the app with a number of tabs that never produced the error and be sure to catch the database exception and send yourself a notification (for example through google analytics) to test if the threading problem does occur in real life usage of the app.
All activity callbacks happen on the main thread, so in the scenario you describe there is no multi-threading going on, no matter how many activities or tabs you have.
ContentProvider doesn't provide any locking. In fact, it can introduce multithreading where you wouldn't already have it because it allows other processes to make calls in to your own process, and when that happens the call is dispatched from a separate thread in your process (not on the main UI thread).
Of course if you create your own threads, then you will also have multi-threading going on.

Categories

Resources