3

I would like to use a SQLite database to store data in an Android application. No problem. But at predefined intervals (every day at 10, 14, 18 and 22 o'clock), I want to run an IntentService that requires access to the database as well. And here comes the problem:

When the user is using the application while the IntentService is started, there will be a problem because there's one database connection active in the Activity and one is to be established in the IntentService. So the database will be locked and the IntentService will fail, right?

How an I achieve that, in these cases, the IntentService just waits for the Activity to finish the current operation (SELECT, UPDATE, etc.) and then runs normally?

In my Activity, I open the database connection (via SQLiteOpenHelper) in onCreate and close it in onDestroy. Does this mean that there's no chance for the IntentService to work on the database in the meantime?

caw
  • 30,999
  • 61
  • 181
  • 291

1 Answers1

4

So the database will be locked and the IntentService will fail, right?

If they perform operations at the same time on separate SQLiteDatabase objects, yes.

How an I achieve that, in these cases, the IntentService just waits for the Activity to finish the current operation (SELECT, UPDATE, etc.) and then runs normally?

Use a single SQLiteOpenHelper instance for both the activity and the service, whether via a singleton, custom Application object, or a ContentProvider.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thank you very much! The custom `Application` class sounds good. But when is creating and closing done when I use this custom class? – caw Jun 04 '12 at 00:35
  • @MarcoW.: You would trigger opening the database in `onCreate()` of the `Application`, or lazy-create it, as you see fit (just do so on a background thread). You would never close it, in all likelihood, just letting the `SQLiteOpenHelper` be discarded along with everything else when the process terminates. – CommonsWare Jun 04 '12 at 10:10
  • It worked perfectly, thank you! Do I really never call `close()` on the database object or the SQLiteOpenHelper? Are data inserted to the database safe from being lost, though? Will they be commited right away? – caw Jun 22 '12 at 08:16
  • @MarcoW.: "Are data inserted to the database safe from being lost, though?" -- yes. "Will they be commited right away?" -- yes. – CommonsWare Jun 22 '12 at 10:22
  • But from time to time, I'm getting `SQLiteCantOpenDatabaseException: unable to open database file` after the call to `getWritableDatabase()`. At least, this error message occurs in my developer console log. – caw Jun 24 '12 at 23:18
  • @MarcoW.: Check for a log message from the sqlite library a few lines above your exception. – CommonsWare Jun 24 '12 at 23:27
  • Wow, thank you very much for the quick reply! And for your help in general :) I only have the stacktrace: `android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file at android.app.ActivityThread.handleBindApplication(ActivityThread.java:3957) at android.app.ActivityThread.access$1300(ActivityThread.java:123) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1185) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4424)` – caw Jun 24 '12 at 23:44
  • @MarcoW.: Well, based on the ICS source code, I fail to see how that stack trace can occur -- `SQLiteCantOpenDatabaseException` does not appear anywhere in `ActivityThread`, let alone in `handleBindApplication()`. However, note that `ActivityThread` is the main application thread, suggesting that you are doing database I/O on that thread in addition to your background thread, somehow. – CommonsWare Jun 25 '12 at 11:11
  • Yes, as you have suggested, I am opening the database connection in my custom application class. And both in my main thread as well as in an `AsyncTask`, I am calling methods such as `update()` and `insert()` on the database object in the custom application class. And it was the part of the stack trace that is caused by the exception (you said the lines above are important). Here's the full trace: http://paste.bradleygill.com/index.php?paste_id=378164 – caw Jun 25 '12 at 11:27
  • @MarcoW.: Um, well, that's rather strange. `onCreate()` of an `Application` is created shortly after any `ContentProvider`s and before anything else, so I would not imagine that you would have anything else trying to work with the database at that moment. My point was that you really should not be doing database I/O on the main application thread, as it will tie up that thread, which could get you in trouble. – CommonsWare Jun 25 '12 at 11:37
  • This bug appears rather infrequent, anyway. While the application is used on more than 14,000 devices, this bug has been reported only 4 times to my Google Play developer console. Could it happen that Android shuts down the application and as the custom application class has no `onStop()` (e.g.) that calls `close()` on the database object, it is still left open and cannot be opened again on next `onCreate()`? – caw Jun 25 '12 at 11:59
  • @MarcoW.: No, you don't need to `close()` the database -- a `ContentProvider` cannot do that, either, and that all works fine. However, since I can't find where `SQLiteCantOpenDatabaseException` is ever thrown, I am out of guesses as to what the cause might be. Sorry! – CommonsWare Jun 25 '12 at 12:23
  • No problem, thanks for your great support, anyway! Maybe it's just a error on part of the user where the device got in trouble and crashed. – caw Jun 25 '12 at 12:25
  • What if service and activity run in different processes? Can they share SQLiteOpenHelper object? – Patryk Dobrowolski Jan 29 '13 at 20:04
  • @PatrykDobrowolski: By definition, no, as two processes cannot share objects. Generally speaking, using multiple processes is not a good idea. – CommonsWare Jan 29 '13 at 20:36
  • @CommonsWare: I was asking because I faced the same problem, but I have service running in distinct process. Finally I decided to create service method called from client to get required data. My problems ended. – Patryk Dobrowolski Jan 31 '13 at 12:47