I am trying to make my Sq-lite database globally accessible throughout my Android application using a singleton pattern:
What are some of the design options for the synchronizing the read/write of a sq-lite database in an android app -My question is how to design the SQLite DB access for the scenario
"using a singleton pattern " as you mentioned, it ensures that only one db connection is made to connect to database.
It's difficult to answer your question, but i think at least there are some things you should
try.
1. Extend the sqliteopenHelper for create database. -> This is the place you should apply the singleton pattern:
e.x:
private DummyDBHelper _instance = null;
public static DummyDBHelper getInstance(Context mContext) {
if(_instance == null) _instance = new DummyDBHelper(mContext);
return _instance;
implement the CRUD operations
Define each object seperately in each file
Call open/close in your activity
Related
I am trying to implement DBFlow for the first time and I think I might just not get it. I am not an advanced Android developer, but I have created a few apps. In the past, I would just create a "database" object that extends SQLiteOpenHelper, then override the callback methods.
In onCreate, once all of the tables have been created, I would populate any lookup data with a hard-coded SQL string: db.execSQL(Interface.INSERT_SQL_STRING);. Because I'm lazy, in onUpgrade() and onDowngrade(), I would just DROP the tables and call onCreate(db);.
I have read through the migrations documentation, which not only seems to be outdated syntactically because "database =" has been changed to "databaseName =" in the annotation, but also makes no mention of migrating from no database to version "initial". I found an issue that claims that migration 0 can be used for this purpose, but I cannot get any migrations to work at this point.
Any help would be greatly appreciated. The project is # Github.
The answer below is correct, but I believe that this Answer and Question will soon be "deprecated" along with most third-part ORMs. Google's new Room Persistence Library (Yigit's Talk) will be preferred in most cases. Although DBFlow will certainly carry on (Thank You Andrew) in many projects, here is a good place to re-direct people to the newest "best practice" because this particular question was/is geared for those new to DBFlow.
The correct way to initialize the database (akin to the SQLiteOpenHelper's onCreate(db) callback is to create a Migration object that extends BaseMigration with the version=0, then add the following to the onCreate() in the Application class (or wherever you are doing the DBFlow initialization):
FlowManager.init(new FlowConfig.Builder(this).build());
FlowManager.getDatabase(BracketsDatabase.NAME).getWritableDatabase();
In the Migration Class, you override the migrate() and then you can use the Transaction manager to initialize lookup data or other initial database content.
Migration Class:
#Migration(version = 0, database = BracketsDatabase.class)
public class DatabaseInit extends BaseMigration {
private static final String TAG = "classTag";
#Override
public void migrate(DatabaseWrapper database) {
Log.d(TAG, "Init Data...");
populateMethodOne();
populateMethodTwo();
populateMethodThree();
Log.d(TAG, "Data Initialized");
}
To populate the data, use your models to create the records and the Transaction Manager to save the models via FlowManager.getDatabase(AppDatabase.class).getTransactionManager()
.getSaveQueue().addAll(models);
To initialize data in DBFlow all you have to do is create a class for your object models that extends BaseModel and use the #Table annotation for the class.
Then create some objects of that class and call .save() on them.
You can check the examples in the library's documentation.
I'm trying to put all the DatabaseRequests inside a module in Android to centralize all the acces to DDBB in the same place.
I'm wondering if I'm making any mistake doing that. The apps works in the right way but I'm concerned about best practices doing that.
I have an static class called DatabaseRequest where all the requests are inside, for instance:
public static void insertUser(Context context, User user) {
DataBaseHelper mDataBaseHelper = OpenHelperManager.getHelper(context, DataBaseHelper.class);
try {
Dao<User, Integer> dao = mDataBaseHelper.getUserDao();
dao.createOrUpdate(user);
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (mDataBaseHelper != null) {
OpenHelperManager.releaseHelper();
}
}
}
The context param is the context of the activity that's making the request.
Is there any performance issue related with this code?
Thanks in advance ;)
No, as Gray (ORMlite creator) said in this post:
is it ok to create ORMLite database helper in Application class?
What is most important with your code is that it guarantees a single
databaseHelper instance. Each instance has it's own connection to the
database and problems happen when there are more than one (1)
connection opened to the database in a program. Sqlite handles
multiple threads using the same connection at the same time but it
doesn't handle multiple connections well and data inconsistencies may
occur.
And in your case you may have multiple connections at one time.
I can preset you my approach on how I'm using ORMlite, I have one singleton class public class DbHelper extends OrmLiteSqliteOpenHelper which takes care of creating database connection and holds all Dao fields. You will have database upgrade code there and some other stuff so consider making facade classes. In my case each facade holds one Dao object for one model class, where i keep logic for complex item retrieving (and for simple cases i just delegate it to Dao object.
Recently I started to get following error in my application. This is NOT in any specific place and I can reproduce only when loop through all data read/write functionality. It comes up pretty much anywhere.
09-14 08:52:15.089: WARN/SQLiteConnectionPool(19268): The connection pool for database '/data/data/com.nnn/databases/data.db' has been unable to grant a connection to thread 1 (main) with flags 0x5 for 30.000002 seconds.
Connections: 0 active, 1 idle, 0 available.
Is there any way to avoid this? I understand that somehow I exhause all connections to database?
I'm using approach #1: http://www.androiddesignpatterns.com/2012/05/correctly-managing-your-sqlite-database.html
And my database code looks like this:
public class DatabaseHelper extends SQLiteOpenHelper
{
private final static String LOG_TAG = "com.nnnn.data.DatabaseHelper";
private static final String DATABASE_NAME = "data.db";
private static final int DATABASE_VERSION = 260;
private static SQLiteDatabase databaseInstance;
public DatabaseHelper()
{
super(MyApplication.Me, DATABASE_NAME, null, DATABASE_VERSION);
}
public static synchronized SQLiteDatabase getDatabase()
{
if (databaseInstance == null) databaseInstance = (new DatabaseHelper()).getWritableDatabase();
return databaseInstance;
}
I have never seen this error. But, I would like to point out that you are not using approach #1 from the page you linked to, and I do believe that your implementation is causing you trouble.
In the design pattern you linked, their approach is to ensure that only one DatabaseHelper instance exists throughout your applications life-cycle. Your approach is different, you are trying to ensure that only one SQLiteDatabase instance exist though-out the applications life-cycle.
So in summery, you are trying to reuse the same database connection for everything(which is not a good idea, and I would strongly suggest you change this approach), but in reality, I believe this approach is causing some issues between SQLiteOpenHelper and your static instance variable.
You are creating new instances of DatabaseHelper in your getDatabase method, sure, your databaseInstance is static, but there is a potential problem with SQLiteOpenHelper instances here. For example, lets say the databaseInstance instance is closed and set to null by a piece of code. Aka, somebody is cleaning up after them-self, then the next time getDatabase() is called, you will create a new database helper, and more importantly, a new SQLiteOpenHelper.
Try removing the synchronized from the getDatabase method. This should not be necessary.
I believe that the only correct way you can handle SQLite DB in Android is to use ContentProvider. For your code you can try to store DatabaseHelper in that static field instead SQLiteDatabase instance, as described in the first approach.
I'm creating an queued upload manager. With this answer to my previous question's guidance, I'll be using a Service, to upload these images. It was recommended that I use a database to keep track of the successfully uploaded, and the pending files.
My initial research leads me to believe that I'll want to create a Bound Service, so I can update my UI once the photos have uploaded, as well as a Started Service, so it can run independent of my Activities that create it. It seems that I'll also need to start it in its own process via the process=":something" directive in the app manifest.
My question is, what would the best way of sharing an SQLite (unless there is a better way) database amongst the N activity clients and the uploader service?
I envision it working like this, in pseudo code:
// in an app
writeRecordToDb( . . . );
// start service
if( service doesn't exist )
{
// start service, and bind
}
// in the service:
if( shared db has another row )
{
doDownload( . . . );
if( download worked )
{
notifyActivity();
if( db has another row )
doDownload( . . . );
}
else
{
retryDownload( . . . );
}
}
Is this the correct way to go about this? I'm again attempting to circumvent the problem of having multiple Activity instances request photo uploads when there is little to no cellular signal. I've just finished reading though the Service and Bound Service docs, and I'm feeling good, but not great.
My initial research leads me to believe that I'll want to create a Bound Service
I wouldn't.
so I can update my UI once the photos have uploaded
You do not need to use the binding pattern to update the UI. You can:
send a local broadcast using LocalBroadcastManager that the activity picks up, or
invoke a PendingIntent supplied in an Intent extra on startActivity() by the activity, or
give Square's Otto event bus a try (looks interesting, but I haven't used it yet)
etc.
as well as a Started Service, so it can run independent of my Activities that create it
Which is why you should not bother with binding, as you do not need that, but you do need to start the service.
My question is, what would the best way of sharing an SQLite (unless there is a better way) database amongst the N activity clients and the uploader service?
Option #1: Keep your SQLiteOpenHelper in a static data member
Option #2: Use a ContentProvider wrapper around your database
Is this the correct way to go about this?
Using a database as a communications channel between components is akin to two next-door neighbors communicating with each other using a banner towed by a biplane. Yes, it works. However, it is slow and expensive.
(also, there's never a biplane when you need one, but I digress...)
If you wish to use a database as a backing store for pending downloads, in case there is some interruption (e.g., user powers down the device) and you wish to pick up those downloads later on, that's fine. However, the service will know what to download by the command you send to it via startService().
CommonsWare covers basically everything you need... but here is some code illustrating the two options just in case there is any confusion.
Keep your SQLiteOpenHelper in a static data member.
public class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper mInstance = null;
private static final String DATABASE_NAME = "databaseName";
private static final String DATABASE_TABLE = "tableName";
private static final int DATABASE_VERSION = 1;
private Context mCxt;
public static DatabaseHelper getInstance(Context ctx) {
/**
* use the application context as suggested by CommonsWare.
* this will ensure that you dont accidentally leak an Activitys
* context (see this article for more information:
* http://developer.android.com/resources/articles/avoiding-memory-leaks.html)
*/
if (mInstance == null) {
mInstance = new DatabaseHelper(ctx.getApplicationContext());
}
return mInstance;
}
/**
* constructor should be private to prevent direct instantiation.
* make call to static factory method "getInstance()" instead.
*/
private DatabaseHelper(Context ctx) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.mCtx = ctx;
}
}
Then in your Service/Activity, keep a reference to your DatabaseHelper and call getInstance() on it.
Wrap the database in a ContentProvider. If you already have one implemented, then you can simply use
mContext.getContentResolver().query(...);
This works because Activity and Service both extend Context (which holds a reference to the ContentResolver).
Some backstory on my applications code organization:
Right now I'm kind of using a ad-hoc ORM for DB access, where each table is split up into a model (just a plain Java class not inheriting from any Android components) which covers normal CRUD activities.
I'm not using a content provider as all this data is private to the application.
I'm keeping a singleton of my databaseopenhelper, and the application context in my application's Application class
class MyApplication extends Application {
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
mContext = this;
dbHelper = new DatabaseHelper(this); // Subclass of SQLiteOpenHelper
}
public static Context getContext(){
return mContext;
}
All activities that touch the db open the database onResume, and close it onPause.
This seemed to be working fine until I started unit testing, when I ran into the first problem with using singletons - breaking encapsulation (specifically not being able use RenamingDelegatingContext to test with a specific test database).
So,
1) Something tells me if I can't unit test properly, my architecture is bunk - but I can't think of a better way to do this (short of passing context and dbhelpers explicitly - which is a pain)
2) If it is not an entirely crazy idea, what would be the best way to go about unit testing this setup?