I met this problem in my own db framework. I have searched this problem in StackOverflow and tried many methods as mentioned, but this problem has not been solved. It worked well in single thread, the problem usually occor in App Widget, when I try to update the list items.
Here is the explanation:
1.this is the SQLiteOpenHelper used to get SQLiteDatabase object, as you see it's singleton:
public class PalmDB extends SQLiteOpenHelper {
public static PalmDB getInstance(final Context context){
if (sInstance == null){
synchronized (PalmDB.class) {
if (sInstance == null) {
sInstance = new PalmDB(context.getApplicationContext());
}
}
}
return sInstance;
}
}
2.Next is the store used to query and save data to database. It is abstract:
public abstract class BaseStore<T extends Model> {
private PalmDB mPalmDatabase = null;
@SuppressWarnings("unchecked")
public BaseStore(Context context) {
this.mPalmDatabase = PalmDB.getInstance(context);
}
protected SQLiteDatabase getWritableDatabase() {
return mPalmDatabase.getWritableDatabase();
}
protected synchronized void closeCursor(Cursor cursor) {
if (cursor == null || cursor.isClosed()) return;
try {
cursor.close();
} catch (Exception e){
LogUtils.d("Couldn't close cursor correctly");
}
}
public synchronized List<T> get(String whereSQL, String orderSQL, Status status, boolean exclude) {
Cursor cursor = null;
List<T> models = null;
SQLiteDatabase database = getWritableDatabase();
try {
cursor = database.rawQuery(" SELECT * FROM " + tableName
+ " WHERE " + BaseSchema.USER_ID + " = " + userId
+ (TextUtils.isEmpty(whereSQL) ? "" : " AND " + whereSQL)
+ (status == null ? "" : " AND " + BaseSchema.STATUS + (exclude ? " != " : " = ") + status.id)
+ (TextUtils.isEmpty(orderSQL) ? "" : " ORDER BY " + orderSQL),
new String[]{});
models = getList(cursor);
} finally {
closeCursor(cursor);
}
return models;
}
protected synchronized List<T> getList(Cursor cursor){
LogUtils.d(this); // print the hash code of this object
LogUtils.d(Thread.currentThread());
List<T> models = new LinkedList<>();
if (cursor != null && !cursor.isClosed() && cursor.moveToFirst()){ // exception here
do {
models.add(getModel(cursor));
} while (cursor.moveToNext());
} else if (cursor != null && cursor.isClosed()) {
LogUtils.e("cursor is closed : " + cursor);
}
return models;
}
private T getModel(Cursor cursor) {
T model = StoreHelper.getBaseModel(cursor, entityClass);
fillModel(model, cursor);
return model;
}
3.Then I override this class for concrete object like this. As you can see that it is also singleton:
public class MindSnaggingStore extends BaseStore<MindSnagging> {
private static MindSnaggingStore sInstance = null;
public static MindSnaggingStore getInstance(Context context){
if (sInstance == null){
synchronized (MindSnaggingStore.class) {
if (sInstance == null) {
sInstance = new MindSnaggingStore(context.getApplicationContext());
}
}
}
return sInstance;
}
private MindSnaggingStore(Context context) {
super(context);
}
}
It worked fine in activities and fragments, but when I include it in App Widget. When I try to remove any entities, usually twice, then it crashed.
Here is the exception:
02-05 20:01:58.497 19317-19330/? E/AndroidRuntime: FATAL EXCEPTION: Binder:19317_1
Process: me.shouheng.notepal, PID: 19317
java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:962)
at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:599)
at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:348)
at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894)
at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:834)
at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:143)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:132)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:219)
at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:258)
at me.shouheng.notepal.provider.BaseStore.getList(BaseStore.java:330)
at me.shouheng.notepal.provider.BaseStore.get(BaseStore.java:127)
at me.shouheng.notepal.provider.BaseStore.get(BaseStore.java:101)
at me.shouheng.notepal.widget.desktop.ListRemoteViewsFactory.getNotes(ListRemoteViewsFactory.java:72)
at me.shouheng.notepal.widget.desktop.ListRemoteViewsFactory.setupModels(ListRemoteViewsFactory.java:63)
at me.shouheng.notepal.widget.desktop.ListRemoteViewsFactory.onDataSetChanged(ListRemoteViewsFactory.java:86)
at android.widget.RemoteViewsService$RemoteViewsFactoryAdapter.onDataSetChanged(RemoteViewsService.java:142)
at com.android.internal.widget.IRemoteViewsFactory$Stub.onTransact(IRemoteViewsFactory.java:49)
at android.os.Binder.execTransact(Binder.java:565)
I did add the sychronized to important methods and the db class is singleton, I wonder why I still get the problem.
More information. Below is my log, I printed the thread, database and store objects:
02-05 22:41:53.119 15712-15712/me.shouheng.notepal D/colorful: ThemeDelegate fetched theme in 1 ms
02-05 22:41:53.305 3030-3149/system_process I/ActivityManager: Displayed me.shouheng.notepal/.activity.TrashedActivity: +235ms
02-05 22:41:53.648 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.NotebookStore@76f9b87
02-05 22:41:53.649 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[main,5,main]
02-05 22:41:53.656 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.NotesStore@f4252
02-05 22:41:53.656 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[main,5,main]
02-05 22:41:58.018 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:41)#<init> ] me.shouheng.notepal.provider.PalmDB@a34edfa
02-05 22:41:58.057 15712-15712/me.shouheng.notepal D/NotePal: [ (AppWidgetUtils.java:20)#NotifyAppWidgets ] Notifies AppWidget data changed for widgets [87, 85]
02-05 22:41:58.061 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.NotebookStore@76f9b87
02-05 22:41:58.062 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[main,5,main]
02-05 22:41:58.071 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.NotesStore@f4252
02-05 22:41:58.072 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[main,5,main]
02-05 22:41:58.155 15712-15712/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:51)#OnCreate ] Created widget 87
02-05 22:41:58.157 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:41)#<init> ] me.shouheng.notepal.provider.PalmDB@a34edfa
02-05 22:41:58.158 15712-15712/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:78)#GetMinds ] me.shouheng.notepal.provider.MindSnaggingStore@f341cfd
02-05 22:41:58.158 15712-15712/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:79)#GetMinds ] Thread[main,5,main]
02-05 22:41:58.160 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.MindSnaggingStore@f341cfd
02-05 22:41:58.161 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[main,5,main]
02-05 22:41:58.169 15712-15712/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:51)#OnCreate ] Created widget 85
02-05 22:41:58.171 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.NotesStore@f4252
02-05 22:41:58.172 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[main,5,main]
02-05 22:41:58.172 15712-16198/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:85)#OnDataSetChanged ] onDataSetChanged widget 87
02-05 22:41:58.173 15712-16198/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:78)#GetMinds ] me.shouheng.notepal.provider.MindSnaggingStore@f341cfd
02-05 22:41:58.173 15712-16198/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:79)#GetMinds ] Thread[Binder:15712_3,5,main]
02-05 22:41:58.174 15712-16198/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.MindSnaggingStore@f341cfd
02-05 22:41:58.175 15712-16198/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[Binder:15712_3,5,main]
02-05 22:41:58.177 15712-15725/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:85)#OnDataSetChanged ] onDataSetChanged widget 85
02-05 22:41:58.178 15712-15725/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.NotesStore@f4252
02-05 22:41:58.178 15712-15725/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[Binder:15712_1,5,main]
02-05 22:41:58.179 3030-4466/system_process W/InputMethodManagerService: Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@86f53ab attribute=null, token = android.os.BinderProxy@8dfbb89
02-05 22:41:58.483 15712-16191/me.shouheng.notepal D/OpenGLRenderer: endAllActiveAnimators on 0xc8ecfc00 (MenuPopupWindow$MenuDropDownListView) with handle 0xc8eb0540
02-05 22:41:58.933 3030-3485/system_process D/AudioService: Stream muted, skip playback
02-05 22:41:59.775 3030-3485/system_process D/AudioService: Stream muted, skip playback
02-05 22:41:59.804 15712-15712/me.shouheng.notepal D/NotePal: [ (AppWidgetUtils.java:20)#NotifyAppWidgets ] Notifies AppWidget data changed for widgets [87, 85]
02-05 22:41:59.807 15712-15725/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:85)#OnDataSetChanged ] onDataSetChanged widget 87
02-05 22:41:59.807 15712-16198/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:85)#OnDataSetChanged ] onDataSetChanged widget 85
02-05 22:41:59.808 15712-15725/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:78)#GetMinds ] me.shouheng.notepal.provider.MindSnaggingStore@f341cfd
02-05 22:41:59.808 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.NotebookStore@76f9b87
02-05 22:41:59.808 15712-15725/me.shouheng.notepal D/NotePal: [ (ListRemoteViewsFactory.java:79)#GetMinds ] Thread[Binder:15712_1,5,main]
02-05 22:41:59.808 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[main,5,main]
02-05 22:41:59.808 15712-16198/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.NotesStore@f4252
02-05 22:41:59.808 15712-15725/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.MindSnaggingStore@f341cfd
02-05 22:41:59.810 15712-16198/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[Binder:15712_3,5,main]
02-05 22:41:59.810 15712-15712/me.shouheng.notepal I/SQLiteConnectionPool: The connection pool for /data/user/0/me.shouheng.notepal/databases/NotePal.db has been closed but there are still 1 connections in use. They will be closed as they are released back to the pool.
02-05 22:41:59.811 15712-15725/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[Binder:15712_1,5,main]
02-05 22:41:59.828 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:327)#GetList ] me.shouheng.notepal.provider.NotesStore@f4252
02-05 22:41:59.829 15712-15712/me.shouheng.notepal D/NotePal: [ (BaseStore.java:328)#GetList ] Thread[main,5,main]
02-05 22:41:59.867 15712-15717/me.shouheng.notepal I/art: Do full code cache collection, code=97KB, data=125KB
02-05 22:41:59.868 15712-15717/me.shouheng.notepal I/art: Starting a blocking GC JitCodeCache
02-05 22:41:59.868 15712-15717/me.shouheng.notepal I/art: After code cache collection, code=79KB, data=87KB
02-05 22:41:59.899 3030-14418/system_process W/InputMethodManagerService: Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@9aae3c6 attribute=null, token = android.os.BinderProxy@8dfbb89
02-05 22:41:59.957 15712-15725/me.shouheng.notepal E/AndroidRuntime: FATAL EXCEPTION: Binder:15712_1
Process: me.shouheng.notepal, PID: 15712
java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:962)
at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:599)
at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:348)
at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894)
at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:834)
at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:143)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:132)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:219)
at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:258)
at me.shouheng.notepal.provider.BaseStore.getList(BaseStore.java:330)
at me.shouheng.notepal.provider.BaseStore.get(BaseStore.java:127)
at me.shouheng.notepal.provider.BaseStore.get(BaseStore.java:101)
at me.shouheng.notepal.widget.desktop.ListRemoteViewsFactory.getMinds(ListRemoteViewsFactory.java:80)
at me.shouheng.notepal.widget.desktop.ListRemoteViewsFactory.setupModels(ListRemoteViewsFactory.java:65)
at me.shouheng.notepal.widget.desktop.ListRemoteViewsFactory.onDataSetChanged(ListRemoteViewsFactory.java:86)
at android.widget.RemoteViewsService$RemoteViewsFactoryAdapter.onDataSetChanged(RemoteViewsService.java:142)
at com.android.internal.widget.IRemoteViewsFactory$Stub.onTransact(IRemoteViewsFactory.java:49)
at android.os.Binder.execTransact(Binder.java:565)