1

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.

1 Answers1

1

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.

Community
  • 1
  • 1
Markus Junginger
  • 6,950
  • 31
  • 52
  • Thanks for responding. I actually read that in another SO question just a little bit ago and have since then updated my 2 calls to DATABASE.Initialize(getApplicationContext()) and made a few other changes to DATABASE.java. I will update my post with those changes. And yes, I tried to keep all my DaoSession stuff in one location. I just generically find the correct entity DAO and call its Insert/Update/etc. – Henry Cowan - 9C Jan 25 '13 at 20:49
  • So, would it really be more beneficial to move my database/daosession stuff to a class that extends Application rather than keep it in a plain java file? When the app is not open and the syncs run, would the onCreate() method of that class still be called? – Henry Cowan - 9C Jan 25 '13 at 22:03
  • What I'm slightly concerned with is, if I move it to that class, would I even be able to close the database and reopen it? I mean, should I even worry about closing the database at all? – Henry Cowan - 9C Jan 25 '13 at 22:23
  • 1
    You don't need to close the database. When Android kills a process, the DB is closed. – Markus Junginger Jan 25 '13 at 22:38
  • But that only happens when the GC runs, right? Considering it is unknown when/if it will run, doesn't it pose the risk of data leaks if it stays open? – Henry Cowan - 9C Jan 25 '13 at 22:43
  • GC won't kill processes... You need to learn some basic Android fundamentals first. Checkout d.android.com – Markus Junginger Jan 25 '13 at 22:57
  • I'm sorry, it's been a long day. What I meant was when Android needs to free up some space, by killing processes and garbage collecting. From what I understood, you have to implicitly call close() on the database, otherwise it will stay open unless Android closes it when it needs free space. Just like static variables retaining their value until it is garbage-collected. – Henry Cowan - 9C Jan 25 '13 at 23:11