Closed database; Automatic sync causes "attempt to re-open an already-closed object: SQLiteDatabase" exception

StackOverflow https://stackoverflow.com/questions/14513852

  •  17-01-2022
  •  | 
  •  

Question

I am creating an Android application using GreenDAO ORM, with a genericized crud service layer on top of that. I have this single file that holds static references to my DaoMaster as well as my SQLiteDatabase:

public class DATABASE {
    private static final String TAG = "DATABASE";
    private static SQLiteDatabase db;
    private static DaoMaster.DevOpenHelper helper;
    private static DaoMaster master;

    public static void Initialize(Context context){
        GetHelper(context);
        GetDatabase();
        GetMaster();
    }

    private static DaoMaster.DevOpenHelper GetHelper(Context context){
        if(helper == null){
            Log.d(TAG, "Helper = null");
            helper =  new DaoMaster.DevOpenHelper(context, Constants.DATABASE_NAME, null);
        }
        return helper;
    }

    private static SQLiteDatabase GetDatabase(){
        if(db == null){
            Log.d(TAG, "Database = null");
            db = helper.getWritableDatabase();
        }
        return db;
    }

    private static DaoMaster GetMaster(){
        GetDatabase();
        if(master == null){
            Log.d(TAG, "Master = null");
            master = new DaoMaster(db);
        }
        return master;
    }

    public static DaoSession GetSession(){
        GetMaster();
        return master.newSession();
    }

    public static void CloseDatabase(){
        try{
            helper.close();
            db = null;
            master = null;
        } catch(Exception e){
            Log.d(TAG, "Failed to close database");
        }
    }
}

Upon launching the app, I call DATABASE.Initialize() and when my main Activity is Destroyed, I call DATABASE.CloseDatabase(). When the app first launches, it performs an initial sync and the entities retrieved are sent through their respective crud service instances, which deals with the respective DAO to persist the entities to this database like so..

public class CrudService<T> {
    private Class<?> _dtoType;
    private Class<T> _entityType;
    private AbstractDao<T, ?> dao;
    private Method _updateMethod;
    private DaoSession session;

    public CrudService(Class<T> entityType){
        _entityType = entityType;
        _dtoType = DtoMapping.getDtoType(entityType); //This gets a dto for the specified entityType via a HashMap - ie: blah -> blahDto

        if(_dtoType != null){
            try {
                _updateMethod = _entityType.getMethod("update", _dtoType); //This finds blah.java's update method that takes in a blahDto as a param - it must be there or you catch
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }

        session = DATABASE.GetSession();
        dao = (AbstractDao<T,?>) session.getDao(entityType);
    }

    public void Insert(T t){
        dao.insertInTx(t);
    }

    public void InsertOrReplace(T t){
        dao.insertOrReplaceInTx(t);
    }

    public void Update(T t){
        dao.update(t);
    }

    public void Delete(T t){
        dao.deleteInTx(t);
    }
    (etc)
}

However, my periodic syncs need access to this database even when the app is not running. Several of the syncs will run pretty often. When a sync runs, in the class that gets called from the syncs, I call DATABASE.Initialize(), but it gives me this error when it reaches my first crud service query:

01-24 18:10:55.304: WARN/System.err(7068): java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/com.example.app/databases/database
01-24 18:10:55.304: WARN/System.err(7068): at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
01-24 18:10:55.304: WARN/System.err(7068): at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1310)
01-24 18:10:55.304: WARN/System.err(7068): at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1253)
01-24 18:10:55.304: WARN/System.err(7068): at de.greenrobot.dao.Query.unique(Query.java:131)
(the rest is where the crud service attempts to query the database and originates in the sync service - omitted for certain reasons)

If anybody can help me figure out what I'm doing wrong, it would be greatly appreciated. I basically just need to know 2 things:

1) Since my variables in DATABASE.java are static, they will be cached until GC runs. Could/should I invoke GC or perhaps finalize(), or will I just have to set them to null in CloseDatabase()?

2) Can I even close my database since my syncs rely upon it and if I don't close it, won't I run into leaks?

Thanks in advance.

EDIT: I have looked around and learned that the static variables in fact DO persist until GC runs. So, I have reworded #1.

EDIT 2: I have since updated my DATABASE.java and the calls to its Initialize method to use the application context and it seems to have cleared things up. I am now getting a NullPointerException in that first crud service query when the sync reruns.

EDIT 3: The null pointer exception has now been fixed. I accidentally caused it to open a new database and the entities didn't exist in it.

Was it helpful?

Solution

You should think about holding the DaoSession object in Application scope. It simplifies things.

Passing correct context to greendao's OpenHelper constructor

https://stackoverflow.com/a/14430803/551269

PS.: Your CrudService looks a bit like DaoSession.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top