I am currently working on an Android project that performs a lot of communication with the SQLite database. I am also trying to implement a MVP framework within the app.
My current implementation of the Singleton instance is similar to the following. (taken from this post: https://github.com/codepath/android_guides/wiki/Local-Databases-with-SQLiteOpenHelper )
public class PostsDatabaseHelper extends SQLiteOpenHelper {
private static PostsDatabaseHelper sInstance;
public static synchronized PostsDatabaseHelper getInstance(Context context) {
if (sInstance == null) {
sInstance = new PostsDatabaseHelper(context.getApplicationContext());
}
return sInstance;
}
private PostsDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
With the existing code above, I call the getInstance method in several Presenter classes, passing into each of them the Context object which was passed on from the Activity/Fragment. The Context objects can be passed across multiple classes.
Instead of the code above, I was thinking of instantiating the databaseHelper only once at the start of the Application, and then all references will then point to a variant of the getInstance method without the context dependency.
EDIT: My main aim here is to remove as much as possible, the presence of the Context object in the Presenter classes, so as to make the code 'cleaner'. Because all the calls to getInstance provide/inject the same type of Context (the Application's context and not an Activity-specific context), I don't see a need to put the Context object as an argument.
public class PostsDatabaseHelper extends SQLiteOpenHelper {
private static PostsDatabaseHelper sInstance;
// called by all other classes
public static synchronized PostsDatabaseHelper getInstance() {
if (sInstance == null) {
//throw error
}
return sInstance;
}
// only called once at the start of the Application
public static void instantiateInstance(Context context){
sInstance = new PostsDatabaseHelper(context.getApplicationContext());
}
private PostsDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
What I would like to know is, will there be any downsides to this approach? Thanks!
You are trading lazy initialization for static initialization.
In general, lazy initialization can amortize the cost of initialization over the life of an application. In this case it seems less important, for two reasons:
It is almost certain that you will need this DB. It seems unlikely that by putting the initialization off, you might avoid having to do it at all.
The Android framework guarantees that the DBHelper constructor can be run from the UI thread: it is not what takes the time. The thing that takes the time is the first call to getWriteableDatabase. The lazy creation of the Helper accomplishes almost nothing.
You might consider making the code even less convoluted, by initializing the DB in the Application like this:
public class DBDrivenApp extends Application implements DBProvider {
// ...
private PostsDatabaseHelper db;
// ...
#Override
public void onCreate() {
super.onCreate();
db = new PostsDatabaseHelper(this);
}
#Override
public PostsDatabaseHelper getDB() { return db; }
// ...
}
... and, better yet, use an IoC framework, like Dagger2, to inject the database instance, so that you can mock it in testing.
Related
I am trying to access application resources, (string resources to be specific) from a Singleton class. Being Singleton, this class cannot hold any reference to Context objects (to prevent memory leak). While I was looking for other implementations on the net, I came across this two implementation:
Create a static context in Application class and use it across the app.
Pass context as a parameter to the method that requires it.
I don't want to use the fist one as it also uses a static reference to Context object. I understand that it's ok to have it statically in the Application class of android, but still it looks like a hack.
The second implementation is useless since i don't have any instance of context which I can pass to the someOtherMethod of the singleton.
So I came up with following implementation where I make my Singleton abstract to override its context requiring methods (for ex. getString(int resId) in the code below) when I initialize the singleton instance.
I am curious to know if this can lead to any memory leaks now?
Where am I confused with this approach:
--> The reference to context in the Overridden getString is final. I am not sure if that can cause a memory leak or not.
public abstract class SingletonClass{
.
.
.
private static SingletonClass sInstance;
private SingletonClass(Context paramContext) {
// constructor code
}
public static SingletonClass getInstance(final Context context) {
if (sInstance == null) {
sInstance = new SingletonClass(context){
#Override
public String getString(int resId) {
return context.getString(resId);
}
};
}
return sInstance;
}
public abstract String getString(int resId);
.
.
.
private void someOtherMethod(){
//uses above getString()
}
}
Your approach does have a memory leak. The first context passed into getInstance will never be garbage collected, since your anonymous class holds a reference to it. (and there is a static reference to the anonymous class). e.g., if you call getInstance(Activity), that activity will remain in memory until the process is killed!
Fortunately there is a pretty easy fix to get rid of the memory leak. You can safely hold onto the application context (context.getApplicationContext), which is basically a singleton context for lifetime of the app.
public static SingletonClass getInstance(Context c) {
if (sInstance == null) {
sInstance = new SingletonClass(c.getApplicationContext());
}
return sInstance;
}
You can depend on activity lifecycle, and require activities to pass reference to your singleton object in onResume method, and clean it in onPause.
protected void onResume() {
super.onResume();
Singleton.getInstance().onResume(this);
}
protected void onPause() {
super.onResume();
Singleton.getInstance().onPause();
}
Also, you can refresh the instance of Context and hold it in WeakReference:
class Singleton {
private WeakReference<Context> mContext;
private boolean hasContext() {
return mContext != null && mContext.get() != null;
}
public static Singleton getInstance(Context c) {
//do your singleton lazy
if (!sInstance.hasInstance()) {
sInstance.mContext = new WeakReference<>(c);
}
return sInstance;
}
}
Second case could hold a reference to finishing activity, so i don't suggest it.
I have a little concern about how I handle my database in my apps, here is basically what I do:
I have a custom class extending SQLiteOpenHelper that handles all the transactions with the DB.
In my app, I have one single Activity and several Fragments that are created, deleted, hidden or shown during the process.
In every Fragment where I need to modify or access data from the DB, I declare a static variable:
private static DatabaseHandler mDB;
And I initialize it this way on the onCreate() methods:
mDB = DatabaseHandler.getInstance(getActivity());
All this is working, my concern is about the variable itself, is it a good idea to declare it as a static variable in all my custom fragment classes?
I also use the same way a class containing the main parameters of the app using mParams = Parameters.getInstance(getActivity());, should I also declare it as static?
I want to avoid memory leaks and NPE but I am not sure what is the right way to handle that.
FYI, the beginning of my DatabaseHandler class:
public class DatabaseHandler extends SQLiteOpenHelper {
private static DatabaseHandler sInstance = null;
(...)
private Resources mResources;
public static DatabaseHandler getInstance(Context context) {
if (sInstance == null) {
sInstance = new DatabaseHandler(context);
}
return sInstance;
}
private DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mResources = context.getResources();
}
(...)
Thank you
I normally create a SQLiteDatabase static instance in my Application class and access across the app.
So , I have a custom class which returns SQLiteDatabase instance on application create.
This is my Application class
public class MainApplication extends Application {
private static SQLiteDatabase mSQLiteDatabase = null;
#Override
public void onCreate(){
super.onCreate();
SQLiteAsyncTask sqLiteAsyncTask = new SQLiteAsyncTask(getApplicationContext());
mSQLiteDatabase = (SQLiteDatabase) sqLiteAsyncTask.loadInBackground();
}
// method to get the sqlite db instance
public SQLiteDatabase getSQLiteInstance(){
return mSQLiteDatabase;
}
}
Now you can get the SQLiteDatabase instance from Activity or Fragment , by
MainApplication.getSQLiteInstance()
I am trying to make my SQLite database globally accessible throughout my Android application using a singleton pattern:
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "foo";
private static final int DATABASE_VERSION = 1;
private static MySQLiteOpenHelper instance;
private MySQLiteOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public static MySQLiteOpenHelper getInstance(Context context) {
if (instance == null) {
instance = new MySQLiteOpenHelper(context);
}
return instance;
}
public static MySQLiteOpenHelper getInstance() throws UnsupportedOperationException {
if (instance == null) {
throw new UnsupportedOperationException();
}
return instance;
}
...
The idea here is that the first call is to getInstance(Context context) passing in the Application object. Thereafter I can just use getInstance() where I can't get a handle on a Context object.
However, I come unstuck when I make the initial MySQLiteOpenHelper.getInstance(getApplication()) call:
java.lang.IllegalStateException: getDatabase called recursively
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:204)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
Can anyone shed any light on what is happening here or suggest a better technique for achieving my goal?
Cheers!
The code you posted is just fine.
java.lang.IllegalStateException: getDatabase called recursively
This is caused when you call e.g. getWritableDatabase() recursively.
One common cause for it is to call getWritableDatabase() in database helper onCreate(). The onCreate() gets invoked when getWritableDatabase() is called and the database does not exist. Calling getWritableDatabase() again is an error. You should use the writable SQLiteDatabase passed to onCreate() as an argument instead.
Your MySQLiteOpenHelper should works fine.
That class is not the problem.
Android documentation for Application states:
There is normally no need to subclass Application. In most situations, static singletons can provide the same functionality [i.e. maintain global application state] in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a Context which internally uses Context.getApplicationContext() when first constructing the singleton.
My request is: Can you explain, and provide code sample that implements the above suggestion for maintaining global state.
Note that there is already a suggestion that recommends subclassing Application:
How to declare global variables in Android?
Thank you.
Correction to StinePike's answer regarding context in the ApplicationState. In the code posted the context passed in to the application state is held on to. If the context passed in is an activity or similar unit then the activity would be leaked and prevented from being garbage collected.
The android documentation for the Application class states you should "internally use Context.getApplicationContext() when first constructing the singleton."
public class ApplicationState {
private Context applicationContext;
private static ApplicationState instance;
private ApplicationState(Context context) {
this.applicationContext = context.getApplicationContext();
}
public static ApplicationState getInstance(Context context) {
if(instance == null) {
instance = new ApplicationState(context);
}
return instance;
}
}
If I am not wrong your are trying to save global variables without extending Application. If so you can do two things
if you don't need any context then you ca simply use a class with static members like this
public class ApplicationState {
public static boolean get() {
return b;
}
public static void set(boolean a) {
b = a;
}
private static boolean b;
}
And if you need a context but you don't want to extend Application you can use
Public class ApplicationState {
private Context context;
private static ApplicationState instance;
private ApplicationState(Context context) {
this.context = context;
public static ApplicationState getInstance(Context context) {
if (instance == null) {
instance = new ApplicationState(context);
}
return instance;
}
public void someMethod(){}
}
So you can call some method like this
ApplicationState.getInstance(context).somemethod();
I'm considering creating a singleton wrapper for a Context so my model objects, if necessary, can open and read from a database connection. My model objects do not have access to a Context, and I'd like to avoid needing to pass a reference to a Context from object to object.
I was planning to place into this singleton a reference to the Context returned by Application.getApplicationContext(). This singleton object would be initialized in my custom Application instance before anything else would need to or have a chance to use it.
Can anyone think of a reason to not do this?
I would urge you to think about what reasons you have for not passing a reference of the application context into your model objects.
There are various well-documented disadvantages of using singletons. I won't go into detail here but you might want to consider:
How singletons limit your ability to properly unit-test your application.
Singletons hide dependencies between different entities in the code- you cannot determine dependecies from inspecting the interfaces.
You have no real control over the lifetime of a singleton, it could exist for the lifetime of your application. Do you really want to potentially hold a DB connection for the lifetime of your app?
The thread safety of your singleton.
If you valid reasons for not passing a reference to a context to other parts of your application then perhaps you should consider some other pattern- a factory might be one possible solution, a service another.
I'm not sure I get your idea, but here's what's been working for me:
public class MyApp extends Application {
private static MyApp instance;
#Override
public void onCreate() {
super.onCreate();
instance = this;
// ...
}
public static MyApp getInstance(){
return instance;
}
// misc helper methods
}
Pasted here to preserve formatting.
public class ContextContainer
{
private static boolean initialized;
private static Context context;
private ContextContainer()
{
//
}
public static synchronized void setApplicationContext(Context context)
{
if (!initialized) {
ContextContainer.context = context;
initialized = true;
} else {
throw new RuntimeException("ApplicationContext has already been set!");
}
}
public static synchronized Context getApplicationContext()
{
return context;
}
}