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?
Related
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.
To implement database access in my application I followed Lars Vogel tutorial, but I'm very confused about a couple of things...
The TodoDbAdapter class has the following constructor and open method:
public TodoDbAdapter(Context context) {
this.context = context;
}
public TodoDbAdapter open() throws SQLException {
dbHelper = new TodoDatabaseHelper(context);
database = dbHelper.getWritableDatabase();
return this;
}
And then this adapter should be initialized like this:
dbAdapter = new TodoDbAdapter(this);
dbAdapter.open();
1) The getWriteableDatabase method is the one responsible by throwing a possible SQLException. Why do we need to rethrow in our open method? Is there a reason for this?
2) What's the point of the whole constructor/open pair? Why not initialize dbHelper and get a database ready for writing in the constructor?
3) Why do we return the instance of the object in the open method with return this? If the open method code were to be moved to the constructor, we no would no longer need to return this, it would be implicit, right? What am I missing here?
1) The getWriteableDatabase method is the one responsible by
throwing a possible SQLException. Why do we need to rethrow in our
open method? Is there a reason for this?
It's a matter of style to explicitly declare runtime exception like this to highlight that open() might fail. If you don't want to handle it, remove the throws clause.
2) What's the point of the whole constructor/open pair? Why not
initialize dbHelper and get a database ready for writing in the
constructor?
This allows you to create the instance (fast operation) without being forced to do the probably slow operation (disk IO, etc.) of opening the database; most of the time this won't matter because you'll do both in one go as in your code snippet. Also, this keeps the constructor exception-free which some people prefer.
3) Why do we return the instance of the object in the open method
with return this? If the open method code were to be moved to the
constructor, we no would no longer need to return this, it would be
implicit, right? What am I missing here?
If it were moved into the constructor, then yes the return this would be implicit. As the usual way to use a DB helper class in Android is to create and open it in one go, open() just does some little builder pattern so you can go TodoDbAdapter helper = new TodoDbAdapter(this).open(); for the most common use case.
To sum up: These three points of yours are mainly about style, there's little functional reason I can think of and definitely other ways to do it that are correct.
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
Dear Fellow Android Developers!
EDIT:
Thank you all for your answers. I see from many of them that it seems to be common (and accepted) practice to write your own close() method in your database adapter. Fair enough.
But how does that work with a ContentProvider? Usually when querying my database through my ContentProvider I simply issue something like:
Cursor managedCursor = managedQuery(...);
I don't see how I, with this methodology, can access the custom close() method in my custom ContentProvider implementation. Should I instead, from my Activity, do something like:
MyCustomProvider myProvider = (MyCustomProvider) getContentResolver();
and then:
myProvider.query(...);
myProvider.close();
And above all; is this at all necessary (as of point 2 below)?
END EDIT
To a certain degree I must say that I get the concept of the SQLiteOpenHelper, what it is, how it's used and so. I even use it on a regular basis when I write my own ContentProvider's.
The thing is that I'm not sure what to do with the SQLiteDatabase object, returned by the myOpenHelper.getWritableDatabase() (or the myOpenHelper.getReadableDatabase() function for what matters) when I'm done with it.
According to Android ContentProvider.onCreate() documentation:
You should defer nontrivial initialization (such as opening, upgrading, and scanning databases) until the content provider is used (via query(Uri, String[], String, String[], String), insert(Uri, ContentValues), etc).
[...]
If you do use SQLiteOpenHelper, make sure to avoid calling getReadableDatabase() or getWritableDatabase() from this method. (Instead, override onOpen(SQLiteDatabase) to initialize the database when it is first opened.)
The above gives me a hint where to initialize the database (the query(...), insert(...), etc functions), but it doesn't tell me anything on how to treat the created SQLiteDatbase object when I've finished using it.
Should I save it as a member variable of my ContentProvider implementation (and treat it much like a "private singleton" for future use)?
Should I just leave it when exiting the query(...), insert(...), etc. functions and trust that the SQLiteOpenHelper will manage it for me in future calls?
[Insert your alternative point-of-view here]
Being the confiding (or lazy) developer I've implemented my code according to the second alternative above. But I can't get rid of the creepy feeling that I'm neglecting something important.
It depends on what you're doing with your database. If you just do an insert, delete or select where you get an business object back, then you can close the database right after using it. As far as I know it is designed that you simply close it and request a new one when ever you need it.
But be careful when you're working with a cursor then you have to keep the database open as long as the cursor is in use. Otherwise the application will crash when the cursor has to reload data.
I guess you should close it, for example in onDestroy() of an activity that is using it.
So in my DBAdapter class I have:
/**
* Close the database
*/
public void close() {
mDb.close(); //mDb was obtained using mDbHelper.getWritableDatabase();
}
And in my activity:
public void onCreate(Bundle bundle){
...
mDBAdapter = new DBAdapter(this);
// Open or create the database
mDBAdapter.open();
}
#Override
public void onDestroy() {
// Close the database
mDBAdapter.close();
super.onDestroy();
}
Not sure if this is suitable for your provider concept.
If you check the example of use for that object in the API of Android, you can see the object is just used, but no close is necesary.
They implement the method close() though, but I havent seen they use it.
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.