Variable database name for ContentProvider - android

I am creating a custom ContentProvider in Android, all the examples I find show the database name being hardcoded, instantiated like this:
public class ItemProvider extends ContentProvider {
private static String DATABASE_NAME = "xyz";
public static class ItemDatabaseHelper extends SQLiteOpenHelper {
ItemDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
What I want to do is use a variable for the database name at runtime, I dont want to hard code the database name in the class. I have tried to find examples of doing this using the ContentProvider class, and have looked through the documentation as well. I can do this if I shed the ContentProvider class and just use a SQLiteOpenHelper class because I can pass in the database name to the constructor as a parameter but I cannot figure out if its possible for ContentProvider. Here is how I get a variable name for the database using SQLiteOpenHelper:
public static class ItemDatabaseHelper extends SQLiteOpenHelper {
ItemDatabaseHelper(Context context, String dbname) {
super(context, dbname, null, DATABASE_VERSION);
}
Can anyone help me get a variable database name for a Content Provider?
Thanks.

I've not really used ContentProviders myself, but looking at the docs I'm guessing the root of the problem is that you don't instantiate the ContentProvider yourself, but that Android does when it is needed to handle a request.
If you want the database name to be defined at runtime by your application, then you could simply use a public static variable, which you set appropriately from another part of your application. You could then reference this from your ContentProvider.
Alternatively, if you're wanting the person who requests something from the ContentProvider to be able to specify the database to query, then from the docs it looks like you could use the path of the request URI to specify the database to query.
The structure of the request URI is detailed here: http://developer.android.com/guide/topics/providers/content-providers.html#urisum and further up this page it says:
The authority is what identifies the provider, not the path; your provider can interpret the path part of the URI in any way you choose.
So as an example, I would imagine you could use an URI of the form:
content://com.example.yourprovider/DB_REF/ID
Then in your implementation of the abstract methods of ContentProvider you could parse the URI to determine the DB to use.
One word of warning though - if you are going to use this approach, then I would suggest not using the value passed in directly. It would be much better to use some kind of validation against a finite list, so that people can't just query any of your databases (if they know their name).
Hopefully that makes sense :)

First, let me say that I've looked at lots of examples for ContentProvider code on the web, and the one that ended up helping me the most was this one:
http://mobile.tutsplus.com/tutorials/android/android-sdk_content-providers/
I have a very similar situation to yours, where I want to have several differently-named databases containing vehicle fuel mileage data. Each of the names consists of a vehicle name (specified by the user) and the current year, plus the fixed text "fuel_data" to make the database filenames human-readable.
Unlike all of the examples I found, I do not create an instance of the database in the ContentProvider's onCreate method. This is because I don't yet know the vehicle name at that point, thanks to the way my code is written (and the fact that ContentProviders are instantiated so early in the Activity lifecycle).
Once I do know the vehicle name (from which I can easily construct the database name), I call a simple little method that I added to my ContentProvider class. As you can see, all it does is close the prior database (if one was open) and open the new one (using the name that I now have available).
I call this method again every time the user selects a new vehicle.
` private static FuelDatabase mDB = null;
public static void switchDatabases( Context context, String newVehicleName )
{
if ( mDB != null )
{
mbB.close();
}
mDB = new FuelDatabase( context, newVehicleName + "." + getCurrentYear( context ) );
}`

Related

Addressing multiple instances of SQLiteOpenHelper when there are multiple Content Providers for a single database

In my app, I have implemented multiple content providers for a single db, one per table, as there are a lot of tables and having the logic of all of them in a single content provider is going to be quite messy.
I followed the advice given by Simo in this link:
Content provider for multiple tables
So there is an abstract AbsShopContentProvider that has a SQLiteOpenHelper member variable. This abstract content provider is then extended by multiple content providers like Table1Provider, Table2Provider,...
So now I have one instance of my SQLiteOpenHelper per Content Provider. Will this create any issues regarding thread safety?
Is it a good idea to make this SQLiteOpenhelper variable "static" in my abstract Content Provider and create an instance of it in onCreate() of the Abstract Provider only if it is null? Will it solve the issue of having many DB helper objects?
All you need is to make sure that you share one instance of SQLiteDatabase , SQLite automatically takes care of locking for same database.
To make a database globally available, extend Application class:
public class App extends Application {
private static SQLiteDatabase db;
public static SQLiteDatabase getDb() {
return db;
}
#Override
public void onCreate() {
super.onCreate();
db = new MySQLiteOpenHelper(getApplicationContext()).getWritableDatabase();
}
}
and add it to manifest:
<application
android:name=".App"
Now, you can access the database from any Activity/Fragment/Service by calling App.getDb()

Random SQLiteConnectionPool error on Android. How to avoid?

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.

Share Database Between Activity and Service

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).

Creating data model class for database handling

I was just starting to work on an database application when I realized I should implement MVC pattern as the application is quite complex and involves a number of database operations.
In regards to this, I have decided to create a separate model class for handling database operations. This class will have all methods which will return me the data after executing Sqlite command(Select for instance) OR will simply execute the SQLite command(Delete for instance). But what I want is to separate this class from Database Adapter class, where I open, create and close my database.
Let me put my concept into code :
public class DataModel
{
/*
Private members
*/
// Method to Select data from Student table
public ArrayList<String> FetchStudents (parameter 1)
{
private ArrayList<String> arrStudent;
DatabaseAdapter objDB= new DatabaseAdapter();
objDB.open();
/*
Some code
*/
objDB.close();
return arrStudent
}
//Method to delete record from Student table
public DeleteStudent(parameter 1)
{
DatabaseAdapter objDB= new DatabaseAdapter();
objDB.open();
//Some code
objDB.close();
}
/*
Rest of methods
*/
}
//DatabaseAdapterClass
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* onCreate method is called for the 1st time when database doesn't exists.
*/
#Override
public void onCreate(SQLiteDatabase db) {
Log.i(TAG, "Creating DataBase: " + CREATE_STUDENT_TABLE);
db.execSQL(CREATE_STUDENT_TABLE);
}
/**
* onUpgrade method is called when database version changes.
*/
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion);
}
}
Question:
What I want to ask is this the correct approach of implementation? Is it fine if create separate class for database methods? What limitations or issues you guys think might trouble me later on? Also, is there a better way to implement the above concept?
Thanks
Stone
What you are referring to as a 'model class' is more commonly known as a data access object (DAO). Your model would usually be a set of classes that hold your data and business logic. In you case, probably a Student class having an ID, name, GPA, etc. properties. If you want to separate data access from your model, you would have your data access class (DatabaseHelper) query the database and use the data it gets to return Student objects or a List<Student>. There is really not much point in separating the data access class from the database helper, it is better to have all of your database-related code in one place.
Using model classes (only), however, may not always be practical on Android, because it has native support for getting and displaying data from a Cursor (CursorAdapter, etc.). If you want to use any of that, you would have to expose your data not as model objects but as Cursor's. As for content providers, have a look at those too, but if you don't need to expose your data to other applications, writing a ContentProvider might be overkill.
On another note, you don't want to be opening and closing the database on each query. It is actually safe to leave it open, it will be automatically closed when your app's process dies.
I do this in my application and it works wonderfully, the code is clean and it doesnt impact performance at all, especially with the hardware phones have today. I tried all of the other approaches and even used a content provider but it just over complicated things in my opinion.
android's native approach data modeling is contentproviders. Link
it kind of abstracts the type of data source as well.
i used to do it in a similar way. but again its also subjective.

What to do with a database, retrieved by a SQLiteOpenHelper implementation, when finished using it?

Dear Fellow Android Developers!
EDIT:
Thank you all for your answers. I see from many of them that it seems to be common (and accepted) practice to write your own close() method in your database adapter. Fair enough.
But how does that work with a ContentProvider? Usually when querying my database through my ContentProvider I simply issue something like:
Cursor managedCursor = managedQuery(...);
I don't see how I, with this methodology, can access the custom close() method in my custom ContentProvider implementation. Should I instead, from my Activity, do something like:
MyCustomProvider myProvider = (MyCustomProvider) getContentResolver();
and then:
myProvider.query(...);
myProvider.close();
And above all; is this at all necessary (as of point 2 below)?
END EDIT
To a certain degree I must say that I get the concept of the SQLiteOpenHelper, what it is, how it's used and so. I even use it on a regular basis when I write my own ContentProvider's.
The thing is that I'm not sure what to do with the SQLiteDatabase object, returned by the myOpenHelper.getWritableDatabase() (or the myOpenHelper.getReadableDatabase() function for what matters) when I'm done with it.
According to Android ContentProvider.onCreate() documentation:
You should defer nontrivial initialization (such as opening, upgrading, and scanning databases) until the content provider is used (via query(Uri, String[], String, String[], String), insert(Uri, ContentValues), etc).
[...]
If you do use SQLiteOpenHelper, make sure to avoid calling getReadableDatabase() or getWritableDatabase() from this method. (Instead, override onOpen(SQLiteDatabase) to initialize the database when it is first opened.)
The above gives me a hint where to initialize the database (the query(...), insert(...), etc functions), but it doesn't tell me anything on how to treat the created SQLiteDatbase object when I've finished using it.
Should I save it as a member variable of my ContentProvider implementation (and treat it much like a "private singleton" for future use)?
Should I just leave it when exiting the query(...), insert(...), etc. functions and trust that the SQLiteOpenHelper will manage it for me in future calls?
[Insert your alternative point-of-view here]
Being the confiding (or lazy) developer I've implemented my code according to the second alternative above. But I can't get rid of the creepy feeling that I'm neglecting something important.
It depends on what you're doing with your database. If you just do an insert, delete or select where you get an business object back, then you can close the database right after using it. As far as I know it is designed that you simply close it and request a new one when ever you need it.
But be careful when you're working with a cursor then you have to keep the database open as long as the cursor is in use. Otherwise the application will crash when the cursor has to reload data.
I guess you should close it, for example in onDestroy() of an activity that is using it.
So in my DBAdapter class I have:
/**
* Close the database
*/
public void close() {
mDb.close(); //mDb was obtained using mDbHelper.getWritableDatabase();
}
And in my activity:
public void onCreate(Bundle bundle){
...
mDBAdapter = new DBAdapter(this);
// Open or create the database
mDBAdapter.open();
}
#Override
public void onDestroy() {
// Close the database
mDBAdapter.close();
super.onDestroy();
}
Not sure if this is suitable for your provider concept.
If you check the example of use for that object in the API of Android, you can see the object is just used, but no close is necesary.
They implement the method close() though, but I havent seen they use it.

Categories

Resources