I have a class extending SQLiteOpenHelper to manage my database stuff. I find it rather tedious to be writing code like this to use my database(DBHelper is the SQLiteOpenHelper object):
DBHelper dbHelper = new DBHelper(context);
FeedResponse feedResponse = dbHelper.getFeedResponse(...);
dbHelper.close();
Is there anything wrong with replacing the above code with a static method and using it like this? Where might I run into trouble when implementing my database access like this?
FeedResponse feedResponse = DBHelper.getFeedResponse(context, ...);
public static FeedResponse getFeedResponse(Context context, ...) {
DBHelper dbHelper = new DBHelper(context);
FeedResponse feedResponse = dbHelper.getFeedResponse(...);
dbHelper.close();
return feedResponse;
}
public FeedResponse getFeedResponse(...) {
//returns data from database
}
Doing this really cuts down on always creating(typing out) a new instance of DBHelper and also closes it without a fuss. It's all taken care of behind the scenes.
Your solution should work, but isn't optimal as you'll create a new DBHelper object for each database query. And the creation of an object is a expensive operation. It would be better if you reuse the DBHelper object.
Also you may have troubles using your DBHelper from multiple threads.
Related
I am trying to write test cases for my app database. This is what I do in the setup method:
#Before
public void testCaseSetUp() {
RenamingDelegatingContext context = new RenamingDelegatingContext(getTargetContext(), "test_");
dbController = new DBController(context);
dbController.open();
}
DBController.java
public DBController(Context c) {
context = c;
}
public DBController open() throws SQLException {
dbHelper = DBHelper.getInstance(context);
database = dbHelper.getWritableDatabase();
return this;
}
RenamingDelegatingContext does not create a new test database instead uses the existing db file. This is causing my test cases to fail as I already have data.
Are you sure that you recreate your SQLiteOpenHelper somewhere inside your DBHelper class? From the looks of it the latter is a singleton, so I can assume you're not recreating it every time. You should do so in your #Before method, because the idea behind using a RenamingDelegatingContext here is that it deletes old databases before opening a new one.
Also it's a good practice to close the SQLiteOpenHelper class after your tests. Something like:
#After
public void tearDown() {
dbHelper.close();
}
If this doesn't work, please post your DBHelper class to verify it's ok.
I am creating an application which makes a lot of interactions with a database (both read and write operations).
To avoid open/close operations at each request, I created a class extending SQLiteOpenHelper with a Singleton design pattern. This way, I am sure only one instance of the SQLiteOpenHelper and only one connection to the database is made during all the application lifecycle (and not only activity lifecycle).
I also read some articles about ContentProvider, but I am not sure it's a better way.
So, this is the main logic of my Singleton class (onCreate and onUpgrade removed) :
public final class BaseSQLite extends SQLiteOpenHelper {
private static BaseSQLite mInstance = null;
private SQLiteDatabase db = null;
public static BaseSQLite getInstance(Context context) {
if (mInstance == null) {
mInstance = new BaseSQLite(context.getApplicationContext(),
DBNAME, DBVERSION);
}
return mInstance;
}
private BaseSQLite(final Context context, final String name,
final int version) {
super(context, name, null, version);
db = getWritableDatabase();
}
#Override
public synchronized void close() {
if (mInstance != null)
db.close();
}
public Cursor getAllData() {
String buildSQL = "SELECT * FROM myTable";
return db.rawQuery(buildSQL, null);
}
}
So, to access my database, I made this :
BaseSQLite baseSQLite = BaseSQLite.getInstance(context);
baseSQLite.getAllData();
It works perfectly for now. But my question is about the close() method. I really don't know when to call it. Actually, my database instance is the same for every Activies of my application, so I think it's a bad idea to call close() in an onPause() method, because the instance will be potentially (and it will often happens) recreated in the onStart() method of the next Activity. Also, I can't detect the end of my application, i.e. when no activity is visible on the screen anymore.
Can somebody give me some help about this issue ? I found some answer when the database is linked to ONE activity, but no really hint is given for my case.
You should call close anytime you are done writing to your database. For example when you insert data, you will have an open connection to the database that should be closed when it is done.
Reading is different. When you create a SQLite database on your phone, the data is persistent. The database exists and the handler you create provides a convenient way to access that information. Reading the database usually takes place by getting a readable instance of the database and using a Cursor to extract values. In that case you close the cursor when you're done, not the database itself.
You're right that you should not be closing the database connection during separate activities' lifecycle methods. Instead, as suggested above, close the database connection in your handler's methods that write to the database when you are done performing that transaction.
I have an SQLite database that is in a separate class from the main class that extends Activity.
I noticed that there are two ways of setting up the database. one way is to put it inside the main Activity class, either in the class or as a nested sub class. the second way is to put it in the separate class.
the separate class looks better, however there is one disadvantage. You have to create an instance of it in the main activity class every time you want to do something. I read that instantiating objects in Android is expensive and should be avoided.
despite this, I would rather make the database as a separate class. Is the cost of instantiating objects enough that it makes putting the database in the same class a better deal?
example of separate class for SQLite database: incomplete psudo-code
public class SQLiteDB {
private static class DbHelper extends SQLiteOpenHelper{
// db helper methods
}
// methods for DB, like get, set, and others
public void openDatabase(){ }
public void closeDatabse(){ }
public void insertRecord(String record){ }
}
example use in main Activity: incompete psudo-code
public class Main extends Activity{
// every time I want to use it I must instantiate an object for the database class
// many instances of SQLiteDB object created, garbage collector works hard
SQLiteDB mDatabase = new SQLiteDB();
openDatabase();
insertRecord("insert this");
closeDatabase();
}
SQLite database in separate class vs. in same class, which is better?
This is very comprehensive question and it depends on more factors(type of application, personal requirements, how you'll deal with db etc.). Somebody can prefer to place database as inner class and someone as separated class. Problem is that many developers are trying to "stick" as much code as possible into one class and maybe they "fear" to create a little more classes. I don't know that exactly. I mentioned that only as my personal note.
But let's back to your question. What is better?
I think that approach with separeted class. You should let your Activity classes only "Activity classes" > only for creating and dealing with UI. Application appearance should be separated from application logic. If you'll follow this "rule" your code will become more clean and human-readable(if someone else will look at your code he shouldn't be completely lost). It's not a shame to have 20 purely written classes as to have all stuff sticked in one class(like a pig).
however there is one disadvantage. You have to create an instance of
it in the main activity class every time you want to do something. I
read that instantiating objects in Android is expensive and should be
avoided.
Did you think about an usage of Singleton? This design pattern is worth to think about it. You will always have only one instance that have many benefits e.q. no waste of memory. I have only good experiences with Singleton. Therefore i recommend you to try and use it.
Example:
private static SQLiteOpenHelper instance;
public static SQLiteOpenHelper getInstance(Context mContext) {
if (instance == null) {
instance = new SQLiteOpenHelperImplementation(mContext);
}
return instance;
}
And at the end i give you a few suggestions:
Everytime you'll work with cursors, databases etc. release / close
them immediately after work is done. This can solve many exceptions
related to SQLiteDatabase and Cursor
An usage of synchronized blocks and methods is pretty good practise
in the case of concurrent programming to avoid many problems
If you have more than one table in database i suggest you create
"serving" class for each table that will wrap CRUD operations and specific
methods of the table
Before Activity is destroyed, check and release all sources which are not
already released.
I prefer the solution you gave here. The primary advantage is that you can easily access the database from any Activity (or other class) in your app. To solve the problem of creating a new instance every time you use the database, you can instead create a single instance in onCreate(), use the database all you want while the Activity is active, and finally close the database in onDestroy().
This would be a matter of personal taste.
However, what I've found to be efficient and clean has been to create a class that extends SQLiteOpenHelper. In this class you will end up writing the SQL code to create your tables and writing methods as your stored procedures.
The class would look something like this:
public class DatabaseInterface extends SQLiteOpenHelper {
// Database version
private static final int DATABASE_VERSION = 1;
public DatabaseInterface(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
//in your oncreate you will write the queries to create your tables
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_NEWS = "CREATE TABLE News(id INTEGER)";
db.execSQL(CREATE_NEWS);
}
// upgrading tables
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// drop tables if exist
db.execSQL("DROP TABLE IF EXSIST " + NEWS);
// recreate tables
onCreate(db);
}
Consider we have a News obj that takes 1 param as it's constructor, your stored procedures can look something like this:
public ArrayList<News> getNews() {
ArrayList<News> mNewes = new ArrayList<News>();
SQLiteDatabase db = null;
Cursor cursor = null;
try {
String sQry = "SELECT * FROM " + NEWS;
db = this.getWritableDatabase();
cursor = db.rawQuery(sQry, null);
if (cursor.moveToFirst()) {
do {
mNewes.add(new News(cursor.getInt(0)));
} while (cursor.moveToNext());
}
} catch (SQLiteException e) {
Log.e("SQLite - getNewes", e.getMessage());
return null;
} finally {
cursor.close();
db.close();
}
return mNewes;
}
In the above method you get and open the application database preform the query on it, anticipating any sql errors and then close the database. Doing it this way assures that you never have any resources open that you don't need/aren't using.
I've used this method in two apps that are currently out in the market and it runs rather quickly with making several hundred calls to methods I created for my stored procedures
Hey! I want to use a singleton class, because if I open the database every activity I get "Leak found"( that happens because I open the database even if it is already open ) . I create a singleton class , but I don't know how should I use it.
Here is my class:
package com.ShoppingList;
import com.ShoppingList.databases.DbAdapter;
public class DbManager {
DbAdapter db;
// singleton
private static DbManager instance = null;
private DbManager() {
}
public static DbManager getInstance() {
if (instance == null)
instance = new DbManager();
return instance;
}
public void setinstance(DbAdapter db){
this.db=db;
}
public DbAdapter getinstancedb(){
return db;
}
}
In the first activity I put :
db = new DbAdapter(this);
db.open();
DbManager.getInstance().setinstance(db);
and for the next activity : DbManager.getInstance().getinstancedb(); but I get force close for second activity.
Can anyone help me how to use it? Thanks...
You can extend Application class and create there an instance of DbAdapter. This way it will be shared by all your activities.
Because db has the same context and life cycle of your first activity. Make your methods public and make them do all the setup/teardown necessary to return your desired result.
regarding the leak warning. Are you closing your db manager connection in onDestroy()?
Whenever i need to create SQLiteOpenHelper 'databasehelper' object, i need to pass the context in function call.
dbUtils.setEntityValues(this, moduleId, sendTo)
the 'this' parameter refers to the activity context. each time new databasehelper object is creating and perform db tasks.
DataBaseHelper dbHelper = new DataBaseHelper(context);
try {
dbHelper.openDataBase();
SQLiteDatabase db = dbHelper.getReadableDatabase();
From class files which are not extending Activity or Application, i cant create databasehelper object. Is there any way to get databasehelper object globally? OR any way to get the application context in classes that are not Activity...?
Im an android beginner, please suggest some tips..
Thanks in advance,
Joe
joe,
You can use getApplicationContext() or getBaseContext() to access the application context from classes that don't extend context.
You will probably also want to read up on this question a bit What's the difference between the various methods to get a Context?
Good luck!
Thanks willytate, I resolved it. the way i do it is...
class myActivity extends Activity {
public static Activity me = null;
...
protected void onCreate(Bundle icicle) {
...
me = this;
...
};
};
class myClass {
void myMthd() {
...
Intent myIntnt = myActivity.me.getIntent();
...
};
};
Now i can create the 'databasehelper' object without passing context like following..
public DataBaseHelper() {
super(myActivity.me.getApplicationContext(), DB_NAME, null, 1);
this.myContext = myActivity.me.getApplicationContext();
}
Thanks again,
Joe