What is the best practice when closing SQLiteDatabase in Android? - 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()
}
}

Related

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.

Android SQlite multithread access

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.

What are the advantages of using adapter, helper for creating or accessing database?

Currntly i am creating SQLite database in android using following code:
SQLiteDatabase db;
try{
SQLiteDatabase dbe = SQLiteDatabase.openDatabase("/data/data/bangla.rana.fahim/databases/dictionary", null,0);
dbe.close();
}
catch(SQLiteException e){
db = openOrCreateDatabase("dictionary", MODE_PRIVATE, null);
db.execSQL("CREATE TABLE IF NOT EXISTS LIST(wlist varchar,ex varchar);");
db.close();
}
And retrieving data using cursor like:
Cursor cc = db.rawQuery(q, null);//q is the desired query.
But i have seen many example of using helper and adapters for the very same purpose. So, my question is what are the benefits of using them?
I guess you are referring to SQLiteOpenHelper, the documentation is pretty clear:
A helper class to manage database creation and version management.
You create a subclass implementing onCreate(SQLiteDatabase),
onUpgrade(SQLiteDatabase, int, int) and optionally
onOpen(SQLiteDatabase), and this class takes care of opening the
database if it exists, creating it if it does not, and upgrading it as
necessary. Transactions are used to make sure the database is always
in a sensible state.
With this you don't have to manually create your database and take care of upgrading it. You just have to implement the two methods onCreate() and onUpgrade() with your database logic and android will make sure to call this methods when is needed. I don't know at what are you referring when you say adapter.
Well it is more concerned about software desing patters. In the SQLiteOpenHelper you do the tasks more related with creating and updating the database when user installs the app or in a db version change.
In an Adapter class which holds the previous class you usually hold a reference to the db and you have the methods to actually interact with it doing the different kind of queries (selects, inserts...).
This way you have you code well encapsulated and therefore it is much easier to maintain.

What to do with a database, retrieved by a SQLiteOpenHelper implementation, when finished using it?

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.

Categories

Resources