sqlite access from multiple threads for reading - android

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

Related

OrmLite inside an Android Module

I'm trying to put all the DatabaseRequests inside a module in Android to centralize all the acces to DDBB in the same place.
I'm wondering if I'm making any mistake doing that. The apps works in the right way but I'm concerned about best practices doing that.
I have an static class called DatabaseRequest where all the requests are inside, for instance:
public static void insertUser(Context context, User user) {
DataBaseHelper mDataBaseHelper = OpenHelperManager.getHelper(context, DataBaseHelper.class);
try {
Dao<User, Integer> dao = mDataBaseHelper.getUserDao();
dao.createOrUpdate(user);
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (mDataBaseHelper != null) {
OpenHelperManager.releaseHelper();
}
}
}
The context param is the context of the activity that's making the request.
Is there any performance issue related with this code?
Thanks in advance ;)
No, as Gray (ORMlite creator) said in this post:
is it ok to create ORMLite database helper in Application class?
What is most important with your code is that it guarantees a single
databaseHelper instance. Each instance has it's own connection to the
database and problems happen when there are more than one (1)
connection opened to the database in a program. Sqlite handles
multiple threads using the same connection at the same time but it
doesn't handle multiple connections well and data inconsistencies may
occur.
And in your case you may have multiple connections at one time.
I can preset you my approach on how I'm using ORMlite, I have one singleton class public class DbHelper extends OrmLiteSqliteOpenHelper which takes care of creating database connection and holds all Dao fields. You will have database upgrade code there and some other stuff so consider making facade classes. In my case each facade holds one Dao object for one model class, where i keep logic for complex item retrieving (and for simple cases i just delegate it to Dao object.

SQLiteDatabase and Cursor leaks and close() calls

I need some help on database and cursor managing. I noticed that, when entering /leaving certain fragments, I get:
W/SQLiteConnectionPool﹕ A SQLiteConnection object for database '+data+data+database' was leaked! Please fix your application to end transactions in progress properly and to close the database when it is no longer needed.
That made me go back from scratch and check what I'm doing and when. I have:
a DatabaseHelper class extending SQLiteOpenHelper, with just some methods for creating and updating the db;
a DatabaseManager class, extending nothing. I use this, among other things, to keep a single reference to a DatabaseHelper object:
public class DatabaseManager {
private DatabaseHelper h; //class extending SQLiteOpenHelper
public DatabaseManager(Context c) {
if (h==null) {
h = new DatabaseHelper(c.getApplicationContext()); }
public Cursor query(...) {
SQLiteDatabase db = h.getReadableDatabase();
return db.rawQuery(...)
}
public void closeConnection() {
SQLiteDatabase db = h.getWritableDatabase();
db.close();
h.close();
}
}
in this class, some methods querying the database and returning a Cursor object;
in this class, a closeConnection() method, which I'm not really sure of.
I use this class from fragments, calling each time new DatabaseManager(getActivity()). This should not create a new helper reference. Right now I am:
calling Cursor.close() as soon as I got the information I wanted from the query;
never calling open() on my helper neither on my SQLiteDatabase, although I read somewhere that should be done. When exactly? Why it all works even without calling it?
calling manager.closeConnection() in the onStop() method of fragments that make use of my database. As you can see, that calls close on h (a reference to the helper class) and on a readable SQLiteDatabase object. However, I'm not really sure about that, because it closes the helper reference h without making it null, so maybe there are some problems with future calls to new DatabaseManager() ? Maybe dealing with database with a singleton pattern does not require you to call h.close()?
Apart from that, needless to say (that's why I'm asking), when switching through fragments I get the above mentioned warning. What's wrong? What should I do? What does end transactions in progress mean? Should I modify my closeConnection() method, call it in different lifecycle times, or don't call it at all?
After embarrassing issue pointed out by #Selvin, I made h static. Now if I remove any call to closeConnection(), it all works well and I don't get any warnings. That means I'm never calling neither h.close() or db.close(). Is that ok? If not, when should I call it?

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.

AsyncTask Implementation

In my application, i need to reduce the time of inserting videos and other details from the database. I'm trying to use the AsyncTask concept in my application. But it gives an error in my insertion part when i do it in doInBackground().Another class does the insertion.
abc.insert(arguments);
where abc is the object of my database class.
Error shown is nullpointerexception in my class where insertion is done.
Is there any solution for this?
thanks,
niki
I don't fully get your case, but it sounds like your are a victim of synchronization issue - several Threads are calling your insertion code.
If this is the case, then a simle solutions is to use Java synchronized statement around your insertion code, e.g.:
public synchronized void insert() { /* code to insert */ }
or
private Object lock = new Object(); // a field of your DBHelper
// somewhere below in your DBHelper
synchronized (lock) {
/* code to insert */
}
If you are simply looking to add data to your database in a worker thread with no UI update post addition then use Java Threads insted of AsynTask.
The sole objective of AsyncTask is to perform the expensive operations in background and update UI after the operation has been performed.
As suggested by Arhimed the database locking can be an issue in your case if you have fired multiple AsyncTasks.

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