How to structure complex Android database - java

What are the best practices to structure big android databases? From what I've seen so far doing some research on this topic, I've seen that most people are using a DatabaseHelper, which extends SQLiteOpenHelper. Does this also apply when you have multiple tables?
For example, in my current project, I have about 30 tables. So far, all of my tables are being created in my DatabaseHelper class, which currently is getting pretty huge. For every module, I also have a (class)Source class, which has all the CRUD methods in that module, and is using a singleton instance of my DatabaseHelper class.
Somehow it feels unnatural to have all of that code in my DatabaseHelper class. Have I done it the right way, or are there some better way to do this?

I would recommend to produce the database via an DB-Client like Squirell or something else. Then you can deploy the database to your app via the assets folder of your app.
Have a look at this thread

I've taken the following approach which helps separate responsibility. First off, start with an abstract database adapter. Its responsibility is to manage access to the database, create tables (if required), handle upgrades, etc. It provides no access to any tables.
public abstract class DBAdapter {
private static final String DATABASE_NAME = "database.db";
private static final int DATABASE_VERSION = 1;
protected final Context context;
protected SQLiteDatabase database;
private DatabaseManager databaseManager;
private static class DatabaseManager extends SQLiteOpenHelper
{
private static final String CREATE_TABLE_PERSON = "create table ...";
//define other tables here
DatabaseManager(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_PERSON);
//create other tables here
}
}
public DBAdapter(Context aContext)
{
context = aContext;
}
public DBAdapter open() throws SQLException {
if (databaseManager == null)
{
databaseManager = new DatabaseManager(context);
}
database = databaseManager.getWritableDatabase();
return this;
}
public void close() {
databaseManager.close();
}
}
Then, for each table, provide an implementation of your DBAdapter. It provides the access to your tables and the column definitions. Alternatively, you can also create DBAdapter implementations for each multi-table joins you need to do. This class provides the create/read/update/delete (CRUD) functionality for this type of access (table or multi-table join).
public class PersonAdapter extends DBAdapter
{
static final String COLUMN__ID = "_id";
static final String TABLE_PERSON = "Person";
static final String COLUMN_DATE_OF_BIRTH = "DateOfBirth";
static final String COLUMN_SURNAME = "Surname";
//etc.
public PersonAdapter(Context aContext) {
super(aContext);
}
public long deletePerson(long personId)
{
open();
long numberRowsDeleted =
database.delete(TABLE_PERSON, COLUMN__ID + "=" + personId, null);
close();
return numberRowsDeleted;
}
//your other Person CRUD methods go here
}

Related

Constructor is not being called in a singleton [duplicate]

This question already has answers here:
Are fields initialized before constructor code is run in Java?
(5 answers)
Closed 12 months ago.
Hi I have the following code and as I am stepping through it in the debugger I notice that the constructor does not get invoked and hence mContext variable initiated within it remains null.
As I am stepping through the debugger the getInstance() function will call the constructor RaceTimeDataContract(Context context), however if I try to step into the constructor it does not and instead the debugger steps to the line where TABLE_NAME is being initialized. The problem is since mContext remains null, then exception is being thrown.
Anyone know what may be causing this behavior? Code is below:
public class RaceTimeDataContract implements BaseColumns{
private static RaceTimeDataContract sInstance;
private static Context mContext;
private RaceTimeDataContract(Context context) {
this.mContext = context; // This is not getting called
}
private RaceTimeDataContract(){}
public static RaceTimeDataContract getInstance(Context context) {
if (sInstance == null) {
sInstance = new RaceTimeDataContract(context.getApplicationContext());
}
return sInstance;
}
// mContext remains null and forces a exception
private final String TABLE_NAME = mContext.getResources().getString(R.string.table_name);
Appreciate any feedback!
When creating new instances, inline field initialisation is performed before the constructor is called. One simple fix in your case is to move the field initialisation into the constructor after the context is set
e.g. (I've reorganised the code a little, and dropped the this on mContext since it is a static member)
public class RaceTimeDataContract implements BaseColumns {
private static RaceTimeDataContract sInstance;
private static Context mContext;
public static RaceTimeDataContract getInstance(Context context) {
if (sInstance == null) {
sInstance = new RaceTimeDataContract(context.getApplicationContext());
}
return sInstance;
}
private final String TABLE_NAME;
private RaceTimeDataContract(Context context) {
mContext = context;
this.TABLE_NAME = mContext.getResources().getString(R.string.table_name);
}
// The below is invalid as it doesn't set TABLE_NAME and should probably be removed
// private RaceTimeDataContract(){}
}
The above isn't very idiomatic and would be better re-arranged to support a singleton better. There are many ways to do this but some general things which would be a benefit are
Storing the context on the instance object, and not statically
Preventing race conditions on getInstance
Removing the default constructor
Assuming you don't retrieve the instance often this can easily be accomplished by synchronising on the getInstance call. e.g.
public class RaceTimeDataContract implements BaseColumns {
private static RaceTimeDataContract sInstance;
public static synchronized RaceTimeDataContract getInstance(Context context) {
if (sInstance == null) {
sInstance = new RaceTimeDataContract(context.getApplicationContext());
}
return sInstance;
}
private final Context mContext;
private final String tableName;
private RaceTimeDataContract(Context context) {
this.mContext = context;
tableName = mContext.getResources().getString(R.string.table_name);
}
}
As a last note it is atypical of a singleton to take a parameter in the getInstance method but always return the same object - you can imagine in this case that if getInstance is called a second time with a different context then we get the original instance which may have an incorrect table name.
If you do need a singleton per context you can store each instance per context in something like a Map<Context, RaceTimeDataContract>, probably using a ConcurrentHashMap, and it would be better to think of it as some kind of reference cache or factory rather than a singletone.
If possible, it is better to just have a zero parameter getInstance method which can retrieve the singleton context statically the first time it is required. This can open your class up to more typical/simpler singleton patterns like enum singletons and static singleton holders.

How can I add methods that I often use to android studio?

For example,
public void show_message(String message){
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
I want this method add auto Activity.java when create new activity or java class.
I want to save different methods like this and include it in the my project quickly where it is needed.
What you should do is create a BaseActivity and make your activity extend this BaseActivity. Add all the default methods in this activity so you can use them everywhere. You can refer this Github project for reference. It uses MVP.
Here is direct link to BaseActivity.
You just need to make a Common Utilities class. Just copy and paste the class in whatever project you are using it. Just make its method access specifiers as public staic so that you can easily access it.
For e.g.
CommonUtilities.showToastMessage(String text);
What I would do is create a config class and store all these small things in it. For example have a look at this :
public class Config {
public Context context;
public String sharedPrefsName;
public String carTablesName, carsTableCarColumn, databaseName;
public int databaseNewVersion, databaseOldVersion;
public boolean showNotificationsToCustomer;
public String customerNotificationState;
public String userMobile;
public SharedPreferences preferences;
public String customerChatTableName;
public String customerChatMessageColumn;
public String customerChatSentByCustomerColumn;
public String customerChatTimeColumn;
public String loggedInUserId;
public String loggedInUserName;
public String customerChatSupportNotifyingUrl;
public Config(Context context) {
this.context = context;
customerChatSupportNotifyingUrl = "";
customerChatTableName = "customerChat";
customerChatMessageColumn = "customerMessage";
customerChatTimeColumn = "sentOn";
customerChatSentByCustomerColumn = "isSentByCustomer";
sharedPrefsName = context.getString(R.string.shared_prefs_login_validator);
preferences = context.getSharedPreferences(sharedPrefsName, Context.MODE_PRIVATE);
customerNotificationState = context.getString(R.string.customer_notification_state);
showNotificationsToCustomer = preferences.getBoolean(customerNotificationState, true);
carTablesName = context.getString(R.string.user_car_table);
carsTableCarColumn = context.getString(R.string.user_car_table_car_column);
databaseName = context.getString(R.string.user_db);
databaseNewVersion = 3;
databaseOldVersion = 1;
loggedInUserId = preferences.getString(context.getString(R.string.user_db), "");
userMobile = preferences.getString(context.getString(R.string.user_mobile), "");
loggedInUserName = preferences.getString(context.getString(R.string.user_name), "");
}
}
I've placed all the constants in a single file so you need not look at them always. If your app grows in size this would be extremely useful.
For using a progress dialog I use a class like this :
public class MyProgressDialog extends ProgressDialog {
String title, message;
public MyProgressDialog(Context context, String title, String message) {
super(context);
if (!title.equals("")) this.setTitle(title);
this.setMessage(message);
this.setCancelable(false);
this.setIndeterminate(false);
}
}
This is nothing but a single class that extends ProgressDialog.So you can aquire all the functionalities of the progress dialog class.
Similarly for toast you could do the same. If you want them to appear when the activity gets created simply keep this:
MyProgressDialog dialog=new MyProgressDialog(this,"title","message");
dialog.show();
in your activity's onCreate() method. You can do the same for toast too.
In case if it is a java class just create a constructor and keep that snippet in that constructor..
You need to read about "File Templates" https://riggaroo.co.za/custom-file-templates-android-studio/ this a large topic, but this is worth it.

Accessing SQL lite object from all activities

I constructed an SQL Lite handler class which extends SQLiteOpenHelper:
public class DatabaseHandler extends SQLiteOpenHelper
The class have this constructor:
public DatabaseHandler(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
How can i use this class from multiple activites? that is throuhout my entire app?
When i try to instantiate the class from another activity it gives an error because the context is wrong.
What is the best practice for excessing a db from multiple activities?
Each activity should have its own instance of SQLiteDatabase.
See Do I need to call getWritableDatabase() everytime I manipulate data for more discussion on the subject, including example code.
Take a global static
Utils.class
public static DatabaseHandler data;
SplashActivity.class
Utils.data=new PortfolioDatabaseManager(getBaseContext());
You can use it as per your need in any class by accessing like:
Utils.data
You can add getReadDatabase & getWriteDatabase in database class:
public SQLiteDatabase getReadDatabase(Context context){
// create or open the database
DatabaseHandler helper = new DatabaseHandler(context);
SQLiteDatabase data = helper.getReadableDatabase();
return data;
}
public SQLiteDatabase getWriteDatabase(Context context){
// create or open the database
DatabaseHandler helper = new DatabaseHandler(context);
SQLiteDatabase data = helper.getWritableDatabase();
return data;
}
You can extend the class with the Application class and after that you can use all the members and functions of that class from any of the activity. You can make your class a member of the Global class and use it in whole application.
Like this :
class Global extends Application
{
........
........
}
When managing persistent data, the best practice in OOP is the use of the "Gateway Pattern".
Gateways encapsulate and centralizes the application database for easier access.
public class OfflineGateway(){
private static OfflineGateway instance;
private Activity activity;
private OfflineGateway(){
//some initialization
}
public static OfflineGateway getInstance(Activity activity){
if(instance == null)
instance = new OfflineGateway();
return instance;
}
//private accessible methods
private String getStringData(){
//do something here
}
private void setStringData(String string){
//do something here
}
}

Garbage Collection with temporary instances of classes

I am relatively new to android development and being from a c# background it is entirely possible my entire strategy is wrong, but I am continually getting warned by Eclipse when degugging that I haven't closed a database connection properly causing a memory leak.
I have a base database class that extends SQLiteOpenHelper:
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
public MySQLiteOpenHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
public MySQLiteOpenHelper(Context context) {
this(context, "myDb", null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE MyTable (A INT)");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public Cursor executeSelect(String sql, String[] parameters) {
return getReadableDatabase().rawQuery(sql, parameters);
}
}
A generic entity:
public class MyClass {
private int a;
public void setA(int value) {
this.a = value;
}
public int getA() {
return this.a;
}
}
And essentially a service for MyClass (although in reality this extends a generic abstract class for reusability purposes)
public class MyClassService {
private MySQLiteOpenHelper helper;
private Context context;
public MyClassService(Context context) {
this.context = context;
}
private MySQLiteOpenHelper getHelper() {
if (helper == null) {
helper = new MySQLiteOpenHelper(this.context);
}
return helper;
}
public void dispose() {
if (helper != null) {
helper.close();
helper = null;
}
}
public ArrayList<MyClass> getAll()
{
ArrayList<MyClass> list = new ArrayList<MyClass>();
Cursor cursor = getHelper().executeSelect("SELECT A FROM MyTable", new String[0]);
while (cursor.moveToNext()) {
MyClass item = new MyClass()
item.setA(cursor.getInt(0));
list.add(item);
}
cursor.close();
return list;
}
}
So, my question is when I use a line of code like this from an Activity:
ArrayList<MyClass> list = new MyClassService(this).getAll();
is the instance of MyClassService disposed of immediately, or could this be the source of my memory leaks.
Would I be better calling the full code to ensure the database is closed using the dispose method?
MyClassService svc = new MyClassService(this);
ArrayList<MyClass> list = svc.getAll();
svc.dispose();
The Garbage Collector would be able to collect your class, as well as the Helper class, since these are no longer part of the Object chain (not a technical term - just something I made up). However, you would still need to explicitly close the database (if you don't this would definitely be your memory leak culprit). As it stands, you can do this in your Object's finalize() method, which is called during garbage collection:
#Override
public void finalize() {
dispose();
}
I usually prefer, however, to do things a little differently. Data Stores like this are often best written as Singletons, since they may be accessed by multiple classes, and if different instances are created they will still open a new access point for reading a writing, and could cause many issues. You sort of have a singleton-style setup in that there is one helper variable in your code, but you may want to simply make your Helper class the singleton. You can do this by removing your constructors and adding this:
private static MySQLiteOpenHelper self;
private MySQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
}
private MySQLiteOpenHelper(Context context) {
this(context, "myDb", null, 1);
}
public static MySQLiteOpenHelper sharedHelper(Context context) {
if (self == null)
self = new MySQLiteOpenHelper(context);
return self;
}
Then, instead of keeping track of a helper object in MyClassService using getHelper(), you can get THE helper using:
MySQLiteOpenHelper.sharedHelper(context);
The benefit of doing this means you only have to keep track of one Helper throughout the entire app, and in the helper's finalize() method, you can now close the database. This will be called when the app process is killed, and prevent any memory leaks:
public void finalize()
{
close();
}
You should explicitly call dispose - not doing so won't cause a memory leak (the object can be collected immediately after new MyClassService(this).getAll() because there aren't any live references to the object), but may cause your database to run out of available connections.
You forgot to call close on the database object returned from getReadableDatabase().

best practice for accessing data in a database while abstracting the accessing

I am attempting to learn the best practices for the following scenario.
I have a defined set of data objects that are subject to change with updates. And some of these objects have arrays of other objects within them.
Using sqlite I have the database set up with the following pattern: Each object is its on table. And if an object has a child object within it, it has a fk to the child table.
The table names and objects are subject to change. So, to make it easy to change those, I need to be able to hide the database calls. I also need the ability to validate the data before inserting into the database. This validation also varies per object and can have different types of validation per object.
Currently I am attempting to have a DAO for each object. And have a DatabaseHelper for each object that uses the ContentProviders to give access to the database. The developers then use the DAO to do their stuff.
It seems to be getting really ugly and complicated. It also seems like there would be an easier way... maybe some trick with ContentProviders.
So, is there anyway to integrate the ContentProvider into a DAO pattern better? Or is there a better way of accomplishing this goal?
Any suggestions are greatly appreciated.
I recently created a data access layer in .net. I created a BusinessObjectBase class as well as a BusinessObjectsBase (plural) class. Moving common functionality into these classes was more challenging than one might initially suppose though. Here are a few tips.
1) Since .Net is a typed language (and Java also), I needed to get type information about the derived class that the base class virtual functions were operating on. In order to do this I used the Curiously Recurring Template Pattern (though I actually hadn't heard of it until I realized its usefuleness on my own): http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern
Basically, it's a generic class that takes its own type as the generic parameter.
2) I relied a lot on reflection. I don't know how much java has to offer in the way of reflection though, or if it is fast enough to be useful for a data access layer. I had to use a free fasterflect library I found on the net since the native reflection methods were too slow.
3) I used PostSharp (Java has Spring to do the same thing) to take care of change tracking so that it would only update objects if they had actually changed.
4) Okay, this is the most important part: Keep it simple. In spite of the fact that I used a weird generic pattern, reflection, and aspect oriented programming to fully accomplish what I wanted, the heart of my dll was actually simpler than you might imagine. I did lots of research to find the perfect orm tool, but found that in the end it wasn't difficult to just write some functions to dynamically generate my own sql statements. This is where the reflection came in handy because I put attributes over the classes which represented tables in the database, and over the properties representing fields in the tables. This way, you only have to change the attribute if the table or field name changes and...
5) I created a short app (almost fits on a single page) to read the database tables/fields and dynamically generate code files with classes for each table.
Okay, so you're probably not wanting to create something that complex, but I thought I'd put some ideas out there from my own experience and maybe you'll find one or more of them useful :)
(As a side note, I know a lot of people will be wondering: why did you go through all that trouble instead of just using an existing ORM. I found existing ORM's to be overly cumbersome to use, and my implementation was more lightweight and faster than any ORM I've looked into)
I always have separate package names for database. I have written one separate Database class which I use in every project. I only change the database name, table name, column name. Following is the example class:
package com.mobisys.android.contactwidget.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class ContactDatabase {
public static final String DATABASE_NAME = "contact.db";
public static final int DATABASE_VERSION = 1;
public static final String CONTACT_TABLE_NAME = "contact";
public static final String KEY_ID = "_id";
public static final String KEY_ROW = "row";
public static final String KEY_COL = "col";
public static final String KEY_APP_WIDGET_ID = "app_widget_id";
public static final String KEY_CONTACT_IMAGE = "image";
public static final String KEY_CONTACT_NAME = "name";
public static final String KEY_CONTACT_NUMBER = "number";
public static final String KEY_CONTACT_EMAIL = "email";
private final OpenHelper contactHelper;
public ContactDatabase(Context context){
contactHelper=new OpenHelper(context);
}
public long insert(String table, ContentValues values){
return contactHelper.getWritableDatabase().insert(table, null, values);
}
public long delete(String table, String where, String[] whereArgs){
return contactHelper.getWritableDatabase().delete(table, where, whereArgs);
}
public int update(String table, ContentValues values, String whereClause, String[] whereArgs){
return contactHelper.getWritableDatabase().update(table, values, whereClause, whereArgs);
}
public long countRows(String query){
return DatabaseUtils.longForQuery(contactHelper.getReadableDatabase(), query, null);
}
public Cursor query(String table,String[] columns, String selection,String[] selectionArgs,String groupBy,String having,String orderBy){
return contactHelper.getReadableDatabase().query(table, columns, selection, selectionArgs, groupBy, having, orderBy);
}
public void close(){
contactHelper.close();
}
private static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE "+
CONTACT_TABLE_NAME+
" ("+ KEY_ID+" INTEGER PRIMARY KEY AUTOINCREMENT, "+
KEY_ROW+" INT, "+
KEY_COL+" INT, "+
KEY_APP_WIDGET_ID+" INT, "+
KEY_CONTACT_IMAGE+" BLOB, "+
KEY_CONTACT_NAME+" TEXT, "+
KEY_CONTACT_NUMBER+" TEXT, "+
KEY_CONTACT_EMAIL+" TEXT"+")");
}
#Override
public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
String alter_query1="alter table "+CONTACT_TABLE_NAME+" RENAME TO temp1;";
db.execSQL(alter_query1);
onCreate(db);
String insert_query1="insert into "+CONTACT_TABLE_NAME+" select * from temp1;";
db.execSQL(insert_query1);
String delete_query1="DROP TABLE temp1;";
db.execSQL(delete_query1);
}
}
}
I also create one HelperDatabase class which have all database related static methods. Example class:
package com.mobisys.android.contactwidget.database;
import android.content.ContentValues;
import android.database.Cursor;
import com.mobisys.android.contactwidget.data.CONTACT;
public class HelperDatabase {
public static long inserContact(CONTACT contact, ContactDatabase database){
ContentValues values=new ContentValues();
values.put(ContactDatabase.KEY_APP_WIDGET_ID, contact.app_widget_id);
values.put(ContactDatabase.KEY_ROW, contact.row);
values.put(ContactDatabase.KEY_COL, contact.col);
values.put(ContactDatabase.KEY_CONTACT_NAME, contact.name);
values.put(ContactDatabase.KEY_CONTACT_NUMBER, contact.cotact_number);
values.put(ContactDatabase.KEY_CONTACT_EMAIL, contact.email);
values.put(ContactDatabase.KEY_CONTACT_IMAGE, contact.image);
long id=database.insert(ContactDatabase.CONTACT_TABLE_NAME, values);
return id;
}
public static void updateMyContactInfo(ContactDatabase contactdb, int _id, String number){
ContentValues values=new ContentValues();
values.put(ContactDatabase.KEY_CONTACT_NUMBER, number);
contactdb.update(ContactDatabase.CONTACT_TABLE_NAME, values, "_id"+"="+_id, null);
}
public static Cursor getContacts(ContactDatabase contactdb, int sort){
if(sort==1)
return contactdb.query(ContactDatabase.CONTACT_TABLE_NAME, null, null, null, null, null, ContactDatabase.KEY_CONTACT_NAME);
else if(sort==2)
return contactdb.query(ContactDatabase.CONTACT_TABLE_NAME, null, null, null, null, null, ContactDatabase.KEY_CONTACT_EMAIL);
else if(sort==3)
return contactdb.query(ContactDatabase.CONTACT_TABLE_NAME, null, null, null, null, null, ContactDatabase.KEY_CONTACT_NUMBER);
return contactdb.query(ContactDatabase.CONTACT_TABLE_NAME, null, null, null, null, null, null);
}
public static boolean isContactExist(ContactDatabase contactdb, String number){
return contactdb.countRows("SELECT COUNT(*) FROM "+ContactDatabase.CONTACT_TABLE_NAME+" WHERE"+ ContactDatabase.KEY_CONTACT_NUMBER + "='"+number+"'")>0;
}
}
So, basically, i can have one class for each database in the project and one HelperDatabase class, which does all inserting, updating, retrieving and deleting functionality.
If my project relies heavily on a database, then it is good practice to have one static object for your database class which will open when your main activity will start and close when your main activity will destroy.
Following is code sample:
public class HomeActivity extends Activity implements View.OnClickListener{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
HelperDatabase.contactdb=new ContactDatabase(this);
startApplication();
}
#Override
public void onDestroy(){
HelperDatabase.contactdb.close();
super.onDestroy();
}
}
Hope, this will be helpful to you.

Categories

Resources