5

I have read a lot of topics about this error message but I can't resolve my problem.

I have an app on google play and I get some error reports from the users. When I am trying the app, everything works fine.

In the application, I am managing a big database with something like 30 tables. I am closing the database in my main activity onDestroy() and all the cursors are closed when the query is finished.

I really don't know why from time to time the users are getting this error message.

Here is the entire error log:

java.lang.IllegalStateException: database /data/data/mdpi.android/databases/LocalDatabase.db (conn# 0) already closed
at android.database.sqlite.SQLiteDatabase.verifyDbIsOpen(SQLiteDatabase.java:2213)
at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1565)
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1525)
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1605)
at mdpi.android.database.LocalDatabase.getHistoryLastSuccessfullUpdate(LocalDatabase.java:661)
at mdpi.android.Journals$7.onItemClick(Journals.java:723)
at android.widget.AdapterView.performItemClick(AdapterView.java:292)
at android.widget.Gallery.onSingleTapUp(Gallery.java:960)
at android.view.GestureDetector.onTouchEvent(GestureDetector.java:1310)
at android.widget.Gallery.onTouchEvent(Gallery.java:937)
at android.view.View.dispatchTouchEvent(View.java:5724)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1964)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1725)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1970)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1739)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2071)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1405)
at android.app.Activity.dispatchTouchEvent(Activity.java:2426)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2019)
at android.view.View.dispatchPointerEvent(View.java:5904)
at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3155)
at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2670)
at android.view.ViewRootImpl.processInputEvents(ViewRootImpl.java:1000)
at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2679)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4517)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:993)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:760)
at dalvik.system.NativeStart.main(Native Method)

And another one:

java.lang.RuntimeException: Unable to start activity ComponentInfo{mdpi.android/mdpi.android.UserInformations}: java.lang.IllegalStateException: database /data/data/mdpi.android/databases/LocalDatabase.db (conn# 0) already closed
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2202)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2237)
at android.app.ActivityThread.access$600(ActivityThread.java:139)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1262)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:4974)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: database /data/data/mdpi.android/databases/LocalDatabase.db (conn# 0) already closed
at android.database.sqlite.SQLiteDatabase.verifyDbIsOpen(SQLiteDatabase.java:2194)
at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1536)
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1496)
at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1576)
at mdpi.android.database.LocalDatabase.getUserInformations(LocalDatabase.java:357)
at mdpi.android.UserInformations.onCreate(UserInformations.java:122)
at android.app.Activity.performCreate(Activity.java:4538)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1071)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2158)
... 11 more

EDIT: One new error.

Today, I got a new error related to the database access:

java.lang.RuntimeException: An error occured while executing doInBackground()

at android.os.AsyncTask$3.done(AsyncTask.java:278)
at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
Caused by: java.lang.NullPointerException
at android.database.sqlite.SQLiteStatement.releaseAndUnlock(SQLiteStatement.java:290)
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:115)
at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1718)
at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1591)
at mdpi.android.database.LocalDatabase.insertCountry(LocalDatabase.java:143)
at mdpi.android.database.CountryTable.EnterCountry(CountryTable.java:21)
at mdpi.android.UserInformations$insertCountryAsync.doInBackground(UserInformations.java:270)
at mdpi.android.UserInformations$insertCountryAsync.doInBackground(UserInformations.java:1)
at android.os.AsyncTask$2.call(AsyncTask.java:264)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
... 4 more
Milos Cuculovic
  • 19,631
  • 51
  • 159
  • 265
  • Just a though. The description of onDestroy() is rather vague. So there is a belief that it may not be called in some non-obvious situations. For example, see the _comments_ to this answer: http://stackoverflow.com/a/9409517/1665128 . So, if you can't reproduce the problem, it might be worthwhile to just close the DB things in another, "more guaranteed" callback. And see. – full.stack.ex Oct 30 '12 at 12:45
  • @full.stack.ex, thank you for the reply. Your idea sounds interesting, but where should I call put hten the database methods, in finish maybe? – Milos Cuculovic Oct 30 '12 at 14:13
  • 1
    it depends on your design I know little about. Playing Captain Banal, looks like the real issue is in an extra _open_ somewhere in the life cycle. It means that creation and destruction must by symmetric. So onResume/onPause or onStart/onStop would be good pairs. And so seem to be onCreate/onDestroy unless there's something like a long living Service plus a static reference to the DB or something like that. I'd branch the code, do my best to place that stuff symmetrically, carefully monkey-(and human-)test, publish and monitor, being ready to publish a rollback update. – full.stack.ex Oct 30 '12 at 16:06
  • @ Milos , I just realized it might be a different thing, but, anyway, it may also be the case. – full.stack.ex Oct 30 '12 at 16:13
  • @full.stack.ex, thank you for the reply and sorry for my late answer. In my code, I am calling the database.open() when I am creating the main activity and database.close() when I am destroying the activity. I think this is symmetric, isn't? – Milos Cuculovic Oct 31 '12 at 07:21
  • 1
    @ Milos, you are right, it is. Still one thing I could imagine is that this symmetry is sometimes broken because of a false assumption. I had a similar problem, and you may have something like that. We had a static object that outlived an activity because of a running service and, under some rare circumstances, was initialized twice. – full.stack.ex Oct 31 '12 at 21:46
  • @full.stack.ex, Hm, yes, it might be the reason. How did you resolved your issue? Where do you think I can open and close the DB? – Milos Cuculovic Nov 01 '12 at 07:45
  • This may sound dumb, but have you trying debugging it to see if or at what point that onDestroy() gets called? Maybe it's being GC in a case of broken reference or something. Never trust the GC. – MLProgrammer-CiM Nov 05 '12 at 08:18
  • @EfEs, I haven't really tested this but normally, the ondestroy is called when the activity is destroyed. I am calling this on my main activity, so when the app is clossed (destroyed), the ondestroy should be called. – Milos Cuculovic Nov 05 '12 at 08:27
  • I come from a C++ background, so don't trust me on this. Sometimes local variables pointing to a class get destroyed when their scope is lost, and the class with them. – MLProgrammer-CiM Nov 05 '12 at 08:45
  • Ah ok, but im am talking about the onDestroy method from the Activity class on android. This method is called when the object from the Activity class is destroyed. Android manages the object life differently than C++. – Milos Cuculovic Nov 05 '12 at 09:28

4 Answers4

1

SQLite database has native interface which is accessible through SQLiteDatabase - this class takes care for synchronizing access to the native SQLite from different threads.
SQLiteDatabase is cached in SQLiteOpenHelper which takes care for providing instance of SQLiteDatabase, it's singleton pattern.

From my experience, you ought to have one instance of SQLiteOpenHelper and get cached SQLiteDatabase from it. Then you don't need to take care for closing/opening SQLiteDatabase.

One instance of SQLiteOpenHelper for the application is necessary to have single instance of SQLiteDatabase in the app. It will solve problems with multi-threaded access to the native database.

In short: singleton of SQLiteOpenHelper from which you get database for each query.
I'm using Roboguice to deal with the ugly singleton pattern.

pawelzieba
  • 16,082
  • 3
  • 46
  • 72
  • Thank you @pawelzieba. Ok, so I have to create a new SQLiteOpenHelper object in each activity where I need to access the database? Right? – Milos Cuculovic Nov 05 '12 at 10:30
  • No, I mean to have a singleton of `SQLiteOpenHelper` for the whole app. The easiest way, in your case, could be to implement it in `android.app.Application` class, I think. – pawelzieba Nov 05 '12 at 11:04
  • Ok, so I have to use something similar to this: http://nerdwa.com/index.php/2011/09/database-sqliteopenhelper-singleton-class-for-android/ and then instanciate the object when I need ? – Milos Cuculovic Nov 05 '12 at 12:46
  • More or less, yes. There is also other solution for your problem: `ContentProvider`. – pawelzieba Nov 05 '12 at 13:02
1

I had a similar problem in a project I worked on.

The project used a Data Access Layer (DAL) which was an abstract class with static methods used for retrieving data from a read-only database. The methods opened a the database, retrieved the data and closed the db. This lead to a exception being thrown when the db was being closed. Not always and not on all phones.

The problem went away when I implemented a content provider and used it instead of the DAL.

wojciii
  • 4,253
  • 1
  • 30
  • 39
1

onDestroy() will only be called when the activity is finished/destroyed/removed from stack. When you are moving out of the activity onStop() will be called. If you need to keep your current design, just call close in onStop(). NOTE: onStop of the current activity will be called after onCreate/onRestart of the new/next activity.

If you have one single DB you can use an SQLiteOpenHelper class as mentioned by pawelieba Also you can have the sqlite db reference in the Application class, and you can just use the reference in all activities, like

((MyApplication)getApplication()).db

Just open the DB in onCreate() of the Application class and close it in onTerminate().

You can also take a look at other SO answers for this error. It has been asked a lot of times before.

java.lang.illegalstateexception database not open android

Android java.lang.IllegalStateException database already closed

Android insert the data SQLite error Caused by java.lang.IllegalStateException: database not open

Community
  • 1
  • 1
Abhishek Nandi
  • 4,265
  • 1
  • 30
  • 43
0

Be sure

  • close cusor after query to get data from sqlite.

  • use transaction in update, insert to sqlite.

But some time maybe we use many JOIN in query sequences, it created temple table to query and maybe it didn't close immediately

Huỳnh Ngọc Bang
  • 1,572
  • 1
  • 19
  • 22