In my content provider I create and maintain 3 SQLiteDatabase objects. They are created like this:
private ContentProviderHelper helper;
#Override
public boolean onCreate() { // that's the ContentProvider onCreate()
SQLiteDatabase dbLog = new DbLog(getContext()).getWritableDatabase();
SQLiteDatabase dbSession = new DbSession(getContext()).getWritableDatabase();
SQLiteDatabase dbLocation = new DbLocation(getContext()).getWritableDatabase();
helper = new ContentProviderHelper(UriManager.getAuthority(getContext()));
helper.addDb(dbLog, DbLog.TABLE_NAME, UriManager.LOG, SQLiteDatabase.CONFLICT_REPLACE);
helper.addDb(dbSession, DbSession.TABLE_NAME, UriManager.SESSION, SQLiteDatabase.CONFLICT_REPLACE);
helper.addDb(dbLocation, DbLocation.TABLE_NAME, UriManager.LOCATION, SQLiteDatabase.CONFLICT_REPLACE);
the ContentProviderHelper stores those SQLiteDatabase in an ArrayList indexed with the UriMatcher.
The <provider> is properly registered in the manifest and my app have SD-card permission.
It runs fine for most of our 500.000 user base, but every once in a while I get a SQLiteCantOpenDatabaseException: unable to open database file from the Google Play
the relevant stack track is:
Caused by: android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file
at android.database.sqlite.SQLiteDatabase.dbopen(Native Method)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1013)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:986)
at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1051)
at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:787)
at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:157)
most often than not those error reports come from generic brandless poor quality devices.
Any help on how to properly avoid those errors will be much appreciated.
edit:
a different SQL exceptions that I'm getting on the same code.
Caused by: android.database.sqlite.SQLiteDiskIOException: disk I/O error: COMMIT;
at android.database.sqlite.SQLiteDatabase.native_execSQL(Native Method)
at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1763)
at android.database.sqlite.SQLiteDatabase.endTransaction(SQLiteDatabase.java:583)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:137)
at ***.***.***.data.ContentManager.onCreate(ContentManager.java:26)
so is there any other way of handling this besides try{} catch(){} the hell out of it ?
edit:
Extra info regarding cursors usage:
In general cursors are kept opened for the minimum amount of time as possible. All 3 SQLiteDatabase have a Wrapper that do the actual Cursor calls, read the data from them and close them. There's only 1 instance where the cursor is passed back to the calling object, but it is an IntentService, again, single thread, single instance, single process (just like ContentProvider) that loops through the cursor and close it.
This doesn't look like a programming fault on your (Java) side, neither of the Android SQLite wrapping classes.
The provided stacktraces and code doesn't provide much information to proof my guess, but I think this should be caused by the unreliable nature of those SDCards (you already mentioned that the observed traces usually come from low quality phones).
The exception itself is generated in the native sqlite code, and while I havn't looked up the C/C++ part of the JNI interface, this should come directly from the underlying sqlite3_open call. For non obvious reasons the actuall errorcode is not included in the thrown exception, so you are basically out of luck here to find the root cause.
As this comes directly from the native layer it's some kind of filesystem/hardware problem. The card may be broken, the cardsocket may be broken/dirty/whatever or anything in between could be messed up (most likely physically).
To give you a possible solution for a problem you can't fix: Don't use the (sometimes unreliable) SDCard. If your databases are small enough (less than a few MBs) you should be fine storing them on the internal flash. This has to be reliable storage as if thouse writes or queries fail the user has more serious problems anyway.
If this is not an option, you could write "most recent data" (I don't know which kind of data you store) internally and migrate this data reguallary on the SDCard. This way you could at least try to store it without flodding your code with try/catch (if you try this in a seperate thread which catches any exception at the top level), but it doesn't solve the read part of the problem.
Another, while a little ugly, thing I can imagine: Just let the exceptions fly right through and catch them in the Application class (you can overload it), push it to disk (internal, obviously) and rethrow it to crash your app. On the next start you could then lookup if you've been crashed by an SQLite Exception and present a msg to the user which states something like "Sorry, your SDCard seems to be broken as we couldn't use it. Consider buying a new one of order a better device". ACRA may help you with this, it does what I tried to describe pretty well the last time I used it (1 year ago).
What I suppose is that you have some problem with concurrency and you are trying to get DB object more than once.
Moreover - having 3 different DB (probably) makes the application run 3 DB engines at a time (memory problems).
There is no single and simple solution while the problem is located in other place than exception is thrown, but some steps that you can do:
Put 3 databases into one file (if possible).
Make a simple singleton to keep DB object in memory and avoid concurrent attempts to the stored file. Remember that SQLite is claimed as "thread safe" not as "multithread" so the IO error can be just a natural behavior to protect DB corruption.
Make sure that cursors are closed as quick as possible (i.e. after query the object read the cursor and map data to some POJO objects.
Workaround - use try/catch while opening db, in case of exception try to do it after some time (1 second).
Related
I am developing a multithreaded server that stores and reads information from a db. The db is implemented with RocksDB.
The problem i am having is that when i access the db from more than one thread at a time, i get that error.
Usually, it ment that the db wasn't deleted after usign it, but now it doesn't make sense, of course it isn't deleted, it's being used by another thread.
I know that the db itself is accessed in secuential order, so tecnically you will never get two threads reading it at the same time, but at least one of them should wait until the lock is released and access it then
anyone has any idea what might happen?
The problem was that i had 2 rocksdb::DB* objects in different threads with the same db name and was opening the db from both threads at the same time
I would like to read some data from the webview.db database that is created when your app uses a WebView. However, that database is managed by classes that are mostly package level or do not provide much in the way of access through apis.
Since I can't share their database objects or locks, how can I safely read content from that database without getting "database is locked" errors or similar exceptions?
I only need to read, I do not need to write.
If there is no way to safely aquire sql queries/cursors on it, is there a way to safely copy the actual webview.db file in a thread safe manner? Is there any danger of getting a corrupt copy?
Thanks much
I think all the locks are at the sqlite level, not in the java code, so you should be able to get away with just opening the db and reading from it without confusing the webview.
However, for the same reason you should be prepared for lock errors and retry your queries until the lock is gone. The db will be locked when the webview does data altering statements.
Hopefully the webview doesn't put an exclusive lock on all the db for the whole time it's running, but i don't see a reason for that.
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.
I have an application that I can insert data into a SQLite database. There's a service thread running every sixty seconds checking if a condition is true and raising an alarm if necessary. Think "calendar" application.
I have lots of activities with a reference to their own SQLiteOpenHelper, and their own SQLiteDatabase object.
The application was working pretty well, but one of the main update applications I decided needed a progress dialog subject - that's another subject, but if you can tell me how I can get the spinner to spin that'd be great. It does however display.
But to get it to display, I needed to put it in a thread.
Creating this thread, I started to get loads of "database is locked" messages in the Log.
I figured the issue may have been related to the fact I had a task in the background running every sixty seconds - even though it visually only took a couple of seconds to do the update, so I placed the main write transactions for the updater function in a beginTransaction block. That's the only place I used one.
Everything seemed good, but then I started getting more spurious errors about the database being locked all over the place.
It's encouraged me to do some tidying up, but it seems after I do the first beginTransaction, any further database modifications I want to make fail with a report that the database is locked, despite that fact I do call db.close. I even close the database helper object and cursor just to be on the safe side.
I was able to clear up most of the warnings with regards to cursors still being open, eg
E/Database( 678): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
I couldn't figure out what was locking the database as everything was being closed.
I then decided to remove the "beginTransaction" which stabilised things - I could now navigate my program again. But Im still getting a few random locking issues.
E/Database( 1304): Failure 5 (database is locked) on 0x26e5d8 when executing 'INSERT INTO
Is it possible to figure out what is locking the database? I can see what is grumbling about the lock, but not what is locking it.
What is the best way to handle concurrent database updates/reads like this? I've read a lot about synchronized and ContentProviders but I must admit, it is a little bit big for me.
Any pointers on the "best" way of doing things?
And can I find out what is locking the database?
Thanks
Simon
class NAME extends SQLiteOpenHelper
add:
#Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
if (!db.isReadOnly()) {
db.execSQL("PRAGMA foreign_keys=ON;");
Cursor c = db.rawQuery("PRAGMA foreign_keys", null);
if (c.moveToFirst()) {
int result = c.getInt(0);
}
if (!c.isClosed()) {
c.close();
}
}
}
You may want to look at the latest version of Berkeley DB as an option. It provides a SQLite3-compatible SQL API (so that your application can stay the same) and it provides a finer granularity of locking and therefore better concurrency. Berkeley DB supports multiple concurrent read and write threads to actively access the database at the same time. You can find out additional information about Berkeley DB on our website.
I'm developing an Android application. It has multiple threads reading from and writing to the Android SQLite database. I am receiving the following error:
SQLiteException: error code 5: database is locked
I understand the SQLite locks the entire db on inserting/updating, but these errors only seem to happen when inserting/updating while I'm running a select query. The select query returns a cursor which is being left open quite a wile (a few seconds some times) while I iterate over it. If the select query is not running, I never get the locks. I'm surprised that the select could lock the db. Is this possible, or is something else going on?
What's the best way to avoid such locks?
You are probably opening and closing multiple database connections in your various threads. This is a bad idea. Just open a single database connection, and reuse it everywhere; SQLite will then ensure that concurrent accesses are serialized correctly.
As with jcwenger's answer, using a ContentProvider is another way of achieving this, but will require much more intrusive changes to your code.
By avoiding leaving cursors open for "quite a while". If you can afford to have all your data in memory all at once, then do so.
If you can't, then try increasing the busy timeout.
Migrate to a ContentProvider rather than directly accessing the DB. ContentResolver marshals away all the threading issues for you and also allows for other useful features like sharing data between apps or syncing with a server.
The api overhead of ContentResolver is minimal. You just need to define an AUTHORITY string (A unique string identifying the "kind" of your data -- use a "com.example.myapp.contacts" type of string) and use ContentResolver.bla rather than db.bla.
Its caused by beginTransaction() function.Look at your code, the problem is solved for my app to making a comment line this function(beginTransaction) line