I'm currently grokking the SQLiteDatabase and related classes with the purpose of getting a better understanding of how it handles concurrency (my current solution works, but I got the impression I might actually be doing redundant work, since its done with the presumption that concurrency is not handled for me).
Right, about the android documentation for SQLiteOpenHelper states the following about the getWriteableDatabase method
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.)
It's a little unclear to me what is being cached(so I currently have a wrapper that does the caching for me). So, what is going on here? Are the two SQLiteDatabase instances wrappers around a cached/static instance?
Also, how is the actual locking handled? If I have two separate SQLiteDatabase instances, created by the same SQLiteOpenHelper instance, are these then transactional safe? As in if i begin a transaction in exclusive mode on one transaction, and then start another transaction in another thread, on the other SQLiteDatabase instance, I would expect the second one to not start until the first one is done. Is this how it works?
"Caching" .. well, I guess you could call it that. The SQLiteOpenHelper typically has a single SQLiteDatabase stored internally (since the last call to create a db), so calling #getWritableDatabase() twice will give you the same object the 2nd time (unless you closed the first instance - then it will create it again). Actually, calling #getReadableDatabase() will attempt to open the database r/w and only fall back on returning a read-only instance if that fails.
Note, closing databases & calling #getWritableDatabase() is not thread-safe - i.e. the SQLiteOpenHelper can return closed databases if you mess with it in many threads.
In short, you're going to have a single instance of SQLiteDatabase - but you can enable locking - making it thread-safe - by calling SQLiteDatabase#setLockingEnabled(true).
It's a little unclear to me what is being cached(so I currently have a wrapper that does the caching for me).
The SQLiteDatabase object is cached.
If I create two SQLiteDatabase instances with this method, and close one, the other one is still open.
Assuming you have just one SQLiteOpenHelper, you cannot "create two SQLiteDatabase instances with this method". The second getWriteableDatabase() call returns the same SQLiteDatabase as does the first call.
If you are only accessing the database from a single component (e.g., just one activity or just one service), use one SQLiteOpenHelper (and, by extension, only one SQLiteDatabase), held by that component. If you are accessing the database from multiple components, you will need to go with a singleton SQLiteOpenHelper instance, either directly or by means of wrapping your database in a ContentProvider.
Related
Ive done a lot of Googlig on this topic and I'm confused as to best practice.
Initially I had:
A) Created a DataBase object in my main class header area and then just passed it to functions as needed. I then later read that a DataBase should be opened and closed each time before use.
so then I:
B) went to each function (passing Context) which uses a SQL command and created a new DataBase object, created a filled cursor via SQL, and then closed the Database before returning. However, I then later read that it's expensive to do this.
now i'm thinking that:
C) I should create a new Database object in each subclass that uses one, and open and close it as needed.
Im sorry for the noob and seemingly design question (delete it if it's out of scope of StackOverflow), however, I truly am confused of how this should be handled to avoid errors, and how Google wants us to do it.
Regards
The Android docs recommend using an SQLiteOpenHelper, which caches the database object. From the SQLiteOpenHelper reference:
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.)
So, I'd go for a singleton carrying an instance of SQLiteOpenHelper, so you can access it from anywhere. Then get the database where you need it, and close it on exit points of your application (if any). That way you only open a database if required, but can re-use the same connection for other tasks as well.
If you do only need the database for single tasks and/or there are other Applications accessing the same database, you may consider closing it directly after your database-tasks are done; there is a discussion about closing in another question.
In my app (a SyncAdapter) I include many ContentProviders for use by other apps and was wondering whether it is appropriate to attempt to share a single SQLiteOpenHelper instance between them all to use? If so, how?
If that's not appropriate, is it considered correct to (as examples seem to indicate) repeat instantiation of the SQLiteOpenHelper within each ContentProvider's onCreate method; seems like there should be a better way!
Is there sufficient information in the Manifest for the Operating System to instantiate ContentProviders without creating their containing Application first? If so, then I can not hold a static SQLiteOpenHelper in a class that extends Application for retrieval by ContentProviders.
Help!
The answer was to merge my ContentProviders into a single ContentProvider that is wired-up to handle my various URIs/tables; here's an example from Google themselves. This way you can instantiate your SQLiteOpenHelper and set it to a field for your overridden methods to use, again, see the example. By the way, another good (in the interests of best practice) pattern I picked up from that code is to: override applyBatch to wrap everything it does in a transaction. Thereafter use it and only it (via. ContentProviderOperations) whenever you want to do any persistence; if you do this you can omit transactions from your actual overridden update, delete and insert methods - because you'll not be using them directly! The latter appeals to me as it simplifies my insert, update and delete methods and ensures that a whole sequence of related changes can be rolled back easily if an Exception is thrown.
There have been a few questions on this topic but none giving firm reasons for why there should or shouldn't be a single or multiple instances of the databaseHelper.
When is it a good idea to have multiple instances of DatabaseHelper and when is it not. Is less complexity (if that is actually the case ) a good enough reason to have just a single instance ?
Your DatabaseHelper should be a singleton for sure. Each helper maintains the single connection to the database. If you have multiple helpers with connections to the same database, then concurrency issues will result. Sqlite does it's own locking underneath the single connection to ensure proper concurrent access to the database so using that single connection (and therefore that single helper) for all of your database operations is recommended and required.
Use one DatabaseHelper for each database in the application. SQLite takes care of file locking for you (many reads, only one write) and Android using Java locking in SQLiteDatabase. However, you can still get have a concurrency failure (simulataneous connections writing at once). It's the multiple connnections part that you want to avoid and that is what is managed by one instance of the DatabaseHelper.
In my android app, I use SQLiteOpenHelper to implements ContentProvider.
Query, add, delete operations are all through ContentProvider.
But in one of my android phone(htc g13), I found *.db-wal file in directory /data/data/[package name]/databases. And the file size increate very fast when operating with ContentProvider. It occupied user RAM space too much.
It is recommended to close the SQLiteOpenHelper to solve my problem (it is useful) in post enter link description here.
But I want to find a "place" to add the "close()" method since I am not using SQLiteOpenHelper directly (using through ContentProvider). query() method in ContentProvider must return a Cursor, and SQLiteDatabse should stay in open state.
I'm confused, what show I do now to keep *.db-wal gone and use ContentProvider normally?
Android framework engineer defer to this view that you need to close the DB.
As per Dianne Hackborn(Android Framework Engineer) in this thread:
A content provider is created when its hosting process is created, and
remains around for as long as the process does, so there is no need to
close the database -- it will get closed as part of the kernel
cleaning up the process's resources when the process is killed.
You have a couple of cases to cover:
1) When your application finishes (e.g. entering onDestroy()) make sure you close all Cursors, Database instances of SQLiteDatabase and SQLiteOpenHelpers (using the model if (connection.isOpen()) object.close())
2) When you application goes onPause() -> onResume() - use this stages appropriately to pause/resume your connection or to close/open them.
It's a good practice to close your database immediately after you finish working with it. The database is cached, so there's no problem closing it and re-acquire instance again when you need it with getWritableDatabase()/getReadableDatabase()
From the official doc:
"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.)"
Also keep in mind that if SQLiteOpenHelper caches and tracks all open instances of SQLiteDatabase, it basically means that if you don't leave open database connections, you won't have to call close on SQLiteOpenHelper.
I recommend closing all cursors and databases immediately after you stop working with them. Always try to enforce try/catch/ for queries operations and a "finally block" to call the close methods on the objects.
We all learn that resources, such as database connections, should be acquired late and released early.
Yet applying this principle to SQLite database connections on Android have caused me some headache.
I have an app that download updates from a backend server in a service working in the background, writing updates to the database at a regular basis. The problem I experience arise when the following sequence occurs:
Service opens a writable database connection
Some activity opens a readable database connection
Service closes its database connection concurrently with the activity reading data
Activity fails due to its database connection was closed
Both the service and the activity uses the same SQLiteOpenHelper class, though different instances, to open their connections. My initial assumption was that this should work just fine, but somehow it seems that the underlying connection is shared between the two database instances.
To work around the problem I ended up not closing the database objects, only closing any opened cursors. This seems to work, though I'm not sure that I'm not leaking memory here.
Is there something obvious I am missing here?
Is there something obvious I am missing here?
I'd say no. Looking at the source code to SQLiteOpenHelper, I can't see how two instances could be sharing a SQLiteDatabase object.
Some diagnostic suggestions:
Dump the toString() value of each SQLiteDatabase, which should give you a Java instance ID. If they are the same, that is where your problem lies, and you will need to work your way upstream to figure out how the heck this is happening (e.g., you really are using the same instance of the SQLiteOpenHelper).
With your database in a stable state (i.e., no need to create or upgrade), flip one of your two database spots to use SQLiteDatabase directly, rather than via SQLiteOpenHelper, and see if that changes matters.