SQLite memory issue with singleton approach - java

I have a SQLite database powering all the content in an Android app.
I have a DatabaseHelper class that extends SQLiteAssetHelper.
I have been having problems with too many instances of my database and then getting a SQLiteCantOpenDatabaseException.
To counter this, I have changed my class to maintain a single instance of the DatabaseHelper object.
I have the following:
private static DatabaseHelper databaseHelper;
public static synchronized DatabaseHelper getInstance(Context context, boolean singleRow, boolean showLoader){
if(databaseHelper == null) {
databaseHelper = new DatabaseHelper(context, singleRow, showLoader);
}
return databaseHelper;
}
public DatabaseHelper(Context context, boolean singleRow, boolean showLoader){
super(context, (new File(DatabaseManager.getDatabasePath(context))).getName(), (new File(DatabaseManager.getDatabasePath(context))).getParentFile().getAbsolutePath(), null, DATABASE_VERSION);
this.context = context;
this.singleRow = singleRow;
this.showLoader = showLoader;
}
I then call the getInstance static method as follows:
DatabaseHelper databaseHelper = DatabaseHelper.getInstance(activity.getApplicationContext(), false, false);
After a certain amount of database activity, the app is still crashing on memory grounds.
I then get this error:
Error Code : 2062 (SQLITE_CANTOPEN_EMFILE)
Caused By : Application has opened two many files. Maximum of available file descriptors in one process is 1024 in default.
(unable to open database file (code 2062))
Having taken the singleton approach, I'm a bit lost for why this is still causing this memory leak.
Any help would be appreciated.

If you are getting a message indicating too many files open, a cause may well be that there are too many Cursor that are still open.
However, the message returned may not always be the same and is probably specific to the task/call being called.
In this case the message was (unable to open database file (code 2062)), yet in another case (from a SELECT the message was unable to open database file (code 14)). SQLite unable to open database file (code 14) on frequent “SELECT” query.
The above link also points to a post I made what quite clearly shows that creating a Cursor results in a file (or files) being opened.
The example was looping through about 500 rows and for each row it was creating/recreating 3 cursors for each row (so potentially 1500+ cursors even though only using 4 cursor objects).
Initially it was only closing the 3 cursors at the end (last row of the parent of all) resulting in the unable to open database File (code 14). Closing the 3 cursors for each iteration resolved the issue.
The code that failed was :-
SQLiteDatabase db = getWritableDatabase();
Cursor shoplistcursor = getAllRowsFromTable(SHOPLIST_TABLE_NAME);
Cursor productcsr;
Cursor aislecsr;
Cursor prdusecsr;
while(shoplistcursor.moveToNext()) {
productcsr = getProductFromProductId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
aislecsr = getAisleFromAisleId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)));
prdusecsr = getProductUsage(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)),
shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
if (productcsr.getCount() < 1 | aislecsr.getCount() < 1 | prdusecsr.getCount() < 1) {
deleteShopListEntry(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_ID)));
}
if(shoplistcursor.isLast()) {
prdusecsr.close();
aislecsr.close();
productcsr.close();
}
}
shoplistcursor.close();
db.close();
}
Whilst the fixed code was :-
SQLiteDatabase db = getWritableDatabase();
Cursor shoplistcursor = getAllRowsFromTable(SHOPLIST_TABLE_NAME);
Cursor productcsr;
Cursor aislecsr;
Cursor prdusecsr;
while(shoplistcursor.moveToNext()) {
productcsr = getProductFromProductId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
aislecsr = getAisleFromAisleId(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)));
prdusecsr = getProductUsage(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_AISLEREF)),
shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_PRODUCTREF)));
if (productcsr.getCount() < 1 | aislecsr.getCount() < 1 | prdusecsr.getCount() < 1) {
productcsr.close();
aislecsr.close();
prdusecsr.close();
deleteShopListEntry(shoplistcursor.getLong(shoplistcursor.getColumnIndex(SHOPLIST_COLUMN_ID)));
} else {
productcsr.close();
aislecsr.close();
prdusecsr.close();
}
}
shoplistcursor.close();
db.close();
}
I tend to now follow the following rule/practice :-
If just getting the result e.g. getting the number of rows, close the Cursor in the method.
If using the Cursor for a display e.g. a ListView, then close the cursor in the activity's onDestroy method.
If using the Cursor for what I'll call more complex processing e.g. deleting rows with underlying references then close the cursors as soon as they are done with, within the processing loop(s).

I really don't know why and really got curious about "Application has opened two many files" error and would like to know what causes it.
However, i use singleton with database without any issues over a year. I used this snippet to get database with singleton in 14 apps and never had any problems.
public class DatabaseManager {
private AtomicInteger mOpenCounter = new AtomicInteger();
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
private DatabaseManager() {
}
public static synchronized DatabaseManager getDatabaseManager(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
return instance;
}
public static synchronized DatabaseManager getDatabaseManager(Context context) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = new DatabaseOpenHelper(context.getApplicationContext());
}
return instance;
}
/**
* Get a writable database
*/
public synchronized SQLiteDatabase openDatabase() {
if (mOpenCounter.incrementAndGet() == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
// System.out.println("DataBaseManager: Database Opened");
} else {
// System.out.println("DataBaseManager: Database Already Open");
}
return mDatabase;
}
public synchronized void closeDatabase() {
if (mOpenCounter.decrementAndGet() == 0) {
// Closing database
mDatabase.close();
// System.out.println("DataBaseManager: Database Closed");
} else {
// System.out.println("DataBaseManager: Database is NOT Closed");
}
}
}
onCreate() i get instance with mDatabaseManager = DatabaseManager.getDatabaseManager(getActivity().getApplicationContext());, onStart() i open database with mDatabaseManager.openDatabase();, and onStop() close it with mDatabaseManager.closeDatabase();

I do the singleton approach as well. There are two ways to access your Data obviously.
You can use a Cursor to populate a list of objects and then close your cursor and then close your DB.
Unless you are returning the cursor for dynamic paging of content because of larger list.
Whether or not to close your connection is contingent upon frequency of usage and your app's specific needs.
However, if you are accessing from new context and sharing the previously created SQLHelper class you could be creating memory leak issues as the constructor requires the context.
It sounds to me like you have too many files open on that single connection though. Have you considered closing your db connection after each interaction. Example:
public static ArrayList<OrderModel> getOrders(Context context){
ArrayList<OrderModel> orderList = new ArrayList<OrderModel>();
SQLiteDatabase db = null;
try{
db = A35DBHelper.openDatabase(context);
String columns[] = {
"*"
};
Cursor cursor = db.query(OrdersTable.TABLE_NAME, columns, OrdersTable.COLUMN_PRIMARY_ID, null, null, null, null);
if (cursor != null) {
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()){
OrderModel order = new OrderModel();
order.setLocalDatabaseId(cursor.getLong(cursor.getColumnIndex(OrdersTable.COLUMN_PRIMARY_ID)));
order.setID(cursor.getString(cursor.getColumnIndex(OrdersTable.COLUMN_REPAIR_ORDER_NUMBER)));
order.setOrderNumber(cursor.getString(cursor.getColumnIndex(OrdersTable.COLUMN_ORDER_NUMBER)));
order.setCreatedAtDate(cursor.getString(cursor.getColumnIndex(OrdersTable.COLUMN_CREATED_AT_DATE)));
order.setImageCount(MediaDataContext.getAllMediaForOrderId(context, order.getID()).size());
order.setDefaultThumbnailUrl(cursor.getString(cursor.getColumnIndex(OrdersTable.COLUMN_DEFAULT_THUMBNAIL_URL)));
orderList.add(order);
}
cursor.close();
}
} catch (Exception ex) {
A35Log.e(TAG, "Failed to get orders: " + ex.getMessage());
}
A35DBHelper.closeDatabase(db);
return orderList;
}
Then my singleton class has the open and close where if the context has changed I new up a new instance of the helper before opening.
Then I use the CloseUtil for try/catch closing each time.
This can still be the same if you are returning the Cursor Object instead of an ArrayList as you are possibly getting dynamic data that handles paging or is to big to fill a list.
But sounds like to me your connection is getting over worked so you may need to revisit your model.

Related

How to call a query when the app is run for the first time in Android Studio

I'm new to Android Studio and SQLite and wanted to run a query when the app is run for the very first time only, and when the app is run again it wont run the query again.
This will depend upon how you are accessing the database.
The most common way is to utilise the SQLiteOpenHelper class. In which case the onCreate method is called when the database is created. The database only ever being created the once (unless it is deleted e.g. the App uninstalled).
When onCreate is called the database will have been created BUT will not have any components (tables, indexes, views, triggers) other than some system components. Typically this is where the components are created but meets the criteria of your question IF using a class that extends SQLiteOpenHelper. However, unless the query is to store data, it would probably be of little use as there would be no data to query.
However, using a class that extends SQLiteOpenHelper is not the only way in which an SQLite database can be accessed, nor would onCreate be called if you were utilising a pre-existing database copied from the assets folder of the package (e.g. using SQLiteAssetHelper).
In these latter cases, you could test to see if the database actually exists and set a flag/indicator. If it does not and then after opening the database you could then check the indicator and run the query.
Demonstration
Here's a demonstration of two methods.
The first using the more typical SQLiteOpenHelper and thus the onCreate method.
The second using an indicator.
The first method utilises a class that extends SQLiteOpenHelper called DBHelper :-
class DBHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "thedatabase.db";
public static final int DATABASE_VERSION = 1;
private static volatile DBHelper instance;
private DBHelper(Context context) {
super(context,DATABASE_NAME,null,DATABASE_VERSION);
this.getWritableDatabase(); //Force database open
}
public static DBHelper getInstance(Context context) {
if (instance==null) {
instance = new DBHelper(context);
}
return instance;
}
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
// Run the query here AFTER creating the components
Log.d("DBHELPER","First run detected");
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
Rather than run a query, data is written to the log from within the onCreate method.
The second method utilises the SQLiteDatabase openOrCreateDatabase (i.e. circumventing using the SQLiteOpenHelper). Thus it has to do some of the things that SQLiteOpenHelper conveniently does automatically:-
class OtherDBHelper {
public static final String DATABASE_NAME = "otherdatabase.db";
public static final int DATABASE_VERSION = 1;
private static boolean databaseCreateIndicator = false;
private static boolean databaseVersionChanged = false;
private SQLiteDatabase database;
private static OtherDBHelper instance;
private OtherDBHelper(Context context) {
if (!context.getDatabasePath(DATABASE_NAME).exists()) {
databaseCreateIndicator = true;
File database_dir = context.getDatabasePath(DATABASE_NAME).getParentFile();
if (!database_dir.exists()) {
database_dir.mkdirs();
}
// Copy database from assets
}
database = SQLiteDatabase.openOrCreateDatabase(context.getDatabasePath(DATABASE_NAME),null);
//* Database now open */
// If utilising Version number check and set the version number
// If not copying pre-existing database create components (tables etc)
if (databaseCreateIndicator) {
//<<<<<<<<<< Query here >>>>>>>>>>
Log.d("OTHERDBHELPER","First run detected");
}
}
public SQLiteDatabase getSQLiteDatabase() {
return database;
}
public static OtherDBHelper getInstance(Context context) {
if (instance==null) {
instance = new OtherDBHelper(context);
}
return instance;
}
}
again the instead of running a query outputting to the log is used.
To test both in unison then some activity code:-
public class MainActivity extends AppCompatActivity {
DBHelper dbHelper;
OtherDBHelper otherDBHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("PREDBMSG","Just prior to getting the databases.");
dbHelper = DBHelper.getInstance(this);
otherDBHelper = OtherDBHelper.getInstance(this);
Log.d("POSTDBMSG","Just after getting the databases.");
}
}
Results
When run for the first time then the log includes:-
2022-10-20 07:09:27.633 D/PREDBMSG: Just prior to getting the databases.
2022-10-20 07:09:27.663 D/DBHELPER: First run detected
2022-10-20 07:09:27.697 D/OTHERDBHELPER: First run detected
2022-10-20 07:09:27.697 D/POSTDBMSG: Just after getting the databases.
When run again :-
2022-10-20 07:10:41.258 D/PREDBMSG: Just prior to getting the databases.
2022-10-20 07:10:41.266 D/POSTDBMSG: Just after getting the databases.

A SQLiteConnection object for database was leaked! Please fix your application

My application give me this warning
A SQLiteConnection object for database
'+data+data+com_example_test+database' was leaked! Please fix
your application to end transactions in progress properly and to close
the database when it is no longer needed.
But I close the db object and the cursor after every use.
try {
while (cursor.moveToNext()) {
...
}
} finally {
if (cursor != null && !cursor.isClosed())
cursor.close();
}
...
db.close();
Can you help me for understand what is the problem?
thanks!!!
UPDATE!
I try this solution from this post
SQLite Connection leaked although everything closed
and I don't have memory leak anymore, is it a good solution?
Possible Solutions:
You have not committed the transactions you have started (You should
always close the transaction once you started)
Check whether you have closed the cursors you have opened if you are
using Sqlite (Looks like you have done this step from the code you posted)
Also move the db.close to finally block
You have not called db.close on a database before deleting it with context.deleteDatabase(...) and then recreating it with dbHelper.getWritableDatabase()
Just drag that db.close up into the finally block.
//Inside your SQLite helper class
#Override
public synchronized void close () {
if (db != null) {
db.close();
super.close();
}
}
//Inside the activity that makes a connection to the helper class
#Override
protected void onDestroy () {
super.onDestroy();
//call close() of the helper class
dbHelper.close();
}
this code stops the leak and fixes cursor problems.
public class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper sInstance;
private static final String DATABASE_NAME = "database_name";
private static final String DATABASE_TABLE = "table_name";
private static final int DATABASE_VERSION = 1;
public static DatabaseHelper getInstance(Context context) {
// Use the application context, which will ensure that you
// don't accidentally leak an Activity's context.
if (sInstance == null) {
sInstance = new DatabaseHelper(context.getApplicationContext());
}
return sInstance;
}
/**
* Constructor should be private to prevent direct instantiation.
* make call to static factory method "getInstance()" instead.
*/
private DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
In my case the error was caused when y try to download new data and database should be updated.
I solved it instantiating the database by calling a SELECT 0. That cause database to be updated, so after that I try to download the new data. And worked fine.
Probably you forgot to remove the break point of debugging
sample:
In my case, I was calling to getWritableDatabase or getReadableDatabase and not use it at all. for example if you use it with "execSQL" execSQL will call "releaseReference" "Releases a reference to the object, closing the object if the last reference was released."

NullPointerException when calling a method from a different class

I'm trying to get a simple note-taking app to work, but I keep on getting a nullPointerException when calling a certain method.
The value from the textEdit in the dialog layout is retrieved just fine.
I checked by logging the results.
The problem occurs when I try to run the createNotes(content) method.
public class Dialog extends Activity {
NotesBean notesBean = null;
public NotesDao datasource;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("log", "Running NotiMgr");
setContentView(R.layout.dialog);
RelativeLayout layout = (RelativeLayout) findViewById(R.id.dialog_background);
layout.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText mEdit = (EditText) v.findViewById(R.id.editText);
String content = mEdit.getText().toString();
Log.v("EditText", content);
//THIS IS WHERE THE ERROR OCCURS
datasource.createNotes(content);
finish();
}
});
}
This class deals with inserts, deletes and db related stuff. I've tried debugging, but it never reaches this class.
public class NotesDao {
public NotesBean createNotes(String content) {
try {
Log.v("createNotes", content);
ContentValues values = new ContentValues();
values.put(DbHelper.COLUMN_CONTENT, content);
long insertSeq = database.insert(DbHelper.TABLE_NOTES, null, values);
Cursor cursor = database.query(
DbHelper.TABLE_NOTES, //String table
allColumns, //String[] columns
DbHelper.COLUMN_SEQ + " = " + insertSeq, //String selection
null, //String[] selectionArgs
null, //String groupBy
null, //String having
null); //String orderBy
cursor.moveToFirst();
NotesBean newNotes = cursorToNotes(cursor);
cursor.close();
return newNotes;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
btw I have also confirmed that the db is opened through logcat...
But it was in a different activity. Does that mean I should close it and open it again in this activity?
Any help would be greatly appreciated! Thanks!
change:
public NotesDao datasource;
to
public NotesDao datasource=new NotesDao();
You have never initialized datasource until you call createNotes(), so the NullPointerException does not come unexpected.
You might consider making NotesDao a Singleton, if you don't need multiple instances of it. Just google Singleton.
you have never initialized 'datasource' object that is why it is giving null pointer exception.
NullPointer Exceptions always come up with missing initializations or wrong view element initialization. In your case you are missing to initialise datasource object (as pointed by earlier answers).
A valuable advice will be always cross check your program for everything is initialised, you may also use Exception Handling for cases you are not sure, like for data coming from different source.
You are not instantiate your NoteDao object. please update your code like this:
public NotesDao datasource=new NotesDao();
As stated, your other class is not initialized.
In your onCreate put the initialization.
datasource = new NotesDao();
You have to wait until your class is made in onCreate to initialize the variables in that class.

Android - Why am I getting NullPointerException in SQLiteOpenHelper onCreate?

I have used LogCat to determine the problem line.
The SQL String I am trying to execute in onCreate is..
CREATE TABLE Routines(_id integer primary key autoincrement, json TEXT);
When it tries to execute this, the problem occurs.
This may well be whats causing the NullPointerException. If you can't see anything wrong with that, please read on for a bit more background.
This database has not been created yet, I keep getting a NullpointerException, I have compared this to previous SQLite code and the problem is proving evasive.
However, I mention this as it still has to go through the onCreate method. I create a new DatabaseOpenHelper (extending SQliteOpenHelper) in my main code and call the helper's open() method as seen below.
public void open() throws SQLException
{
ssDatabase = databaseOpenHelper.getWritableDatabase();
}
If I am not mistaken, as my database has not been created (I made sure of that through uninstalling it prior). The onCreate SQLiteHelper is invoked when that open() method is called.
This is the code where I call that open method.
try
{
dbConnector = new DatabaseConnector (this);
debug = "2 ";
// This on first start will invoke the database onCreate method - throws SQLException
dbConnector.open(); /* PROBLEM LINE */
debug = "3 ";
}
catch (Exception e)
{
textView1.setText(debug + e.toString());
}
And this is the code containing my DatabaseOpenHelper and onCreate method
private class DatabaseOpenHelper extends SQLiteOpenHelper
{
public DatabaseOpenHelper(Context context, String name, CursorFactory factory, int version)
{
super(context, name, factory, version);
Log.i(TAG, "Constructor");
}
// On initial creation of database
#Override
public void onCreate(SQLiteDatabase db)
{
Log.i(TAG, "Before SQL command");
String sqlCreateCommand = "CREATE TABLE Routines"
+ "(_id integer primary key autoincrement, "
+ "json TEXT);";
Log.i(TAG, sqlCreateCommand);
// I believe this to be the PROBLEM LINE
ssDatabase.execSQL(sqlCreateCommand);
Log.i(TAG, "Jab done");
}
// On upgrade - currently do nothing
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{}
}
Thanks for the help!
seems like i dont have the prvilege to just post a comment. One question: ssDatabase.execSQL(sqlCreateCommand); --> shouldn't this be db.execSQL(sqlCreateCommand);
since the parameter is SQLiteDatabase db. Secondly you use _id. is this index not created by default ? Nevermind if this has nothing to do with it. This were just my thoghts about it. :)

Ormlite setup without using base activities

I'm using ORMLite in an android project, and I'm not wanting to use the extended activities because I'm inserting values into the database on an AsyncTask.
In the docs it says:
"If you do not want to extend the OrmLiteBaseActivity and other base classes then you will need to duplicate their functionality. You will need to call OpenHelperManager.getHelper(Context context, Class openHelperClass) at the start of your code, save the helper and use it as much as you want, and then call OpenHelperManager.release() when you are done with it."
It also says to add the database helper class in the strings.xml, which I have. So I'm not sure what I'm doing wrong.
I'm using a class called DataAccess for my data tier that looks like this:
public class DataAccess {
private Context context;
private DBHelper dbHelper;
public DataAccess(Context _context) {
this.context = _context;
dbHelper = getDBHelper(_context);
}
private DBHelper getDBHelper(Context context) {
if (dbHelper == null) {
dbHelper = (DBHelper) OpenHelperManager.getHelper(context, DBHelper.class);
}
return dbHelper;
}
}
And I'm using the extended helper class:
public class DBHelper extends OrmLiteSqliteOpenHelper {
private static final String DATABASE_NAME = "database.db";
private static final int DATABASE_VERSION = 1;
private Dao<SomeObject, Integer> someObjectTable = null;
private ConnectionSource connectionSource = null;
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
this.connectionSource = connectionSource;
try {
TableUtils.createTable(connectionSource, SomeObject.class);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
#Override
public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
}
public Dao<SomeObject, Integer> getSomeObjectDao() throws SQLException {
if (someObjectTable == null) {
dateTable = getDao(SomeObject.class);
}
return someObjectTable;
}
The idea is to create the DataAccess class and have it create the DBHelper if it hasn't already.
Can someone tell me if this is right or wrong, or if I'm on the right path?
Thanks!
I'm using ORMLite in an android project, and I'm not wanting to use the extended activities because I'm inserting values into the database on an AsyncTask.
You are on the right track but a little off #Matt. Frankly I'd never done a project without extending our base classes. But it is a good exercise so I've created this ORMLite example project which uses an Activity and manages its own helper.
Your DBHelper class is fine but really you do not need your DataAccess class. In each of your activities (or services...) you will need to have something like the following:
private DBHelper dbHelper = null;
#Override
protected void onDestroy() {
super.onDestroy();
if (dbHelper != null) {
OpenHelperManager.releaseHelper();
dbHelper = null;
}
}
private DBHelper getHelper() {
if (dbHelper == null) {
dbHelper = (DBHelper)OpenHelperManager.getHelper(this, DBHelper.class);
}
return dbHelper;
}
You [obviously], then use this in your code by doing something like:
Dao<SomeObject, Integer> someObjectDao = getHelper().getSomeObjectDao();
So whenever you call getHelper() the first time, it will get the helper through the manager, establishing the connection to the database. Whenever your application gets destroyed by the OS, it will release the helper -- possibly closing the underlying database connection if it is the last release.
Notice that the OpenHelperManager.getHelper() needs the Context as the first argument in case you do this without even an Activity base class.
Edit:
If you do want to create a DataAccess type class to centralize the handling of the helper class then you will need to make the methods static and do your own usage counter. If there are multiple activities and background tasks calling getHelper() then the question is when do you call releaseHelper()? You'll have to increment a count for each get and only call release when the counter gets back to 0. But even then, I'm not 100% sure how many lines you'd save out of your activity class.
I could nitpick but essentially you are doing it correct.
The call
dbHelper = (DBHelper) OpenHelperManager.getHelper(context, DBHelper.class);
Looks up the DBHelper class and instantiates it for the context. If you have defined it in your strings.xml, you can leave off the DBHelper.class at the end.
onUpgrade in you DBHelper.java, you may want to consider dropping the table you create in onCreate and then calling onCreate (to make sure you don't have conversion issues from update to update). You could do a more complex update if you wanted.
Other than that, it looks good. If you end up wanting data accessory methods for your DB objects beyond the base DAO methods, you will eventually want to create more thorough implementations of your object DAOs, but this is a good start.

Categories