17

I recently updated one of my (open-source) Android apps and my users are getting an exception that I can't replicate. The key parts are :

android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

and then

Caused by: android.database.sqlite.SQLiteException: Failed to change locale for db '/data/data/com.airlocksoftware.hackernews/databases/hacker_news_cache.db' to 'en_US'.

This is happening on devices with Android 2.3 - 4.2.1, and in multiple places within the app where I try to connect to a database. I am closing the database after I use it.

I can't find much information about the "failed to change locale for db" exception. When I look at the source for SQLiteConnection (line 386) it seems to be a problem with either the 'android_metadata' table or 'updating the indexes using a new locale'.

Here is the code that's causing the exception (on Github).

java.lang.RuntimeException: An error occured while executing doInBackground()
at android.support.v4.content.ModernAsyncTask$3.done(ModernAsyncTask.java:137)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
at java.util.concurrent.FutureTask.run(FutureTask.java:239)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:856) Caused by: android.database.sqlite.SQLiteException: Failed to change locale for db '/data/data/com.airlocksoftware.hackernews/databases/hacker_news_cache.db' to 'en_US'.
at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:386)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:218)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:854)
at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:229)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
at com.airlocksoftware.database.DbInterface.(DbInterface.java:28)
at com.airlocksoftware.hackernews.loader.StoryLoader.loadStories(StoryLoader.java:62)
at com.airlocksoftware.hackernews.loader.StoryLoader.loadInBackground(StoryLoader.java:54)
at com.airlocksoftware.hackernews.loader.StoryLoader.loadInBackground(StoryLoader.java:1)
at android.support.v4.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:240)
at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:51)
at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:40)
at android.support.v4.content.ModernAsyncTask$2.call(ModernAsyncTask.java:123)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
... 3 more Caused by: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:548)
at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:364)
... 24 more
liftdeadtrees
  • 441
  • 1
  • 4
  • 16
  • I experience exactly the same problem and only on one of few devices. It doesn't depend on Locale set on the device, the exception is thrown whichever is set. Do you access the database from AsyncTask? And do you have two or more tables in one `SQLiteDatabase` object (which you possibly acces simultaneously)? – Marcel Bro Jan 31 '13 at 23:03
  • @anoniim I'm accessing the database from a Loader, so yeah it's done on another thread. This specific code should only be accessing one table, but there might be other threads accessing other tables at the same time. Do you think that's the issue? – liftdeadtrees Feb 01 '13 at 04:25
  • Please show the source for the `CacheDbOpener`? – t0mm13b Feb 01 '13 at 14:36
  • @t0mm13b [Here's the source](https://github.com/bishopmatthew/HackerNews/blob/master/src/com/airlocksoftware/hackernews/cache/CacheDbOpener.java) for `CacheDbOpener` – liftdeadtrees Feb 01 '13 at 22:18
  • I guess, you are using a `getContext()` within the `AsyncLoader`, might be best to try use a static `Context` outside of the scope, i.e. declared at Application level or activity level, and use that when calling `CacheDbOpener(static_Context)` for example, and see what results you get? – t0mm13b Feb 01 '13 at 22:25
  • Do you open and close the database just before / after the request? I've just got the idea it could be caused by closing the database while there is another thread accessing it. – Marcel Bro Feb 04 '13 at 13:40
  • That could be it -- I switched my SQLiteOpenHelper over to using a singleton as per http://www.androiddesignpatterns.com/2012/05/correctly-managing-your-sqlite-database.html I was never able to replicate the error on my device (so it's hard to test), but so far I haven't seen this error again. Will update with this as the answer once more of my users have upgraded. – liftdeadtrees Feb 05 '13 at 03:57

4 Answers4

4

check your database content, specially 'android_metadata'. it's a table with this DDL:

CREATE TABLE android_metadata ( 
    locale TEXT 
);

which should contain at least one record with this content:

en_US

this solved my own problem.

for more details:
open your database file with a sqlite editor such as SQLiteStudio then open android_metadata table (if it does not exist create it. (you may create it with the query editor (tools>open query editor) and copy/paste the DDL code above)

for inserting the record you may copy/paste this line in the query editor:

insert into android_metadata values ('en_us');

hint: to run the query in SQLiteStudio you should push the button with lightening icon in the toolbar.

Ali Ghanavatian
  • 506
  • 1
  • 6
  • 14
2

The key is android.database.sqlite.SQLiteDatabaseLockedException.

I had the same issue, and it kept increasing with the increase in Database Locking.

What to do? - Eliminate Database locking.

How? - Make sure no concurrent write calls take place, and

You may read more about avoiding database locking here and in this very well explained SO answer.

I would suggest shifting all your write calls on a single thread, this way avoiding locking. You could use AsyncQueryHandler for this.

Community
  • 1
  • 1
user2520215
  • 603
  • 8
  • 15
1

If you're changing your locale in the app, make sure you change it before you open your database.

I was having this same issue in my app when I was switching the locale after opening my database, and it was trying to switch to a different database based on the locale, which didn't exist.

LukeWaggoner
  • 8,869
  • 1
  • 29
  • 28
0

Check if you have the same error on another phone. If the error doesn't appear there, it's probably that your phone cached something. Delete the database if possible and restart the phone.

It worked for me then.

user4157124
  • 2,809
  • 13
  • 27
  • 42