I wanna know how to design lazyholder singleton pattern with arguments.
I know only when no argument there is but dont know with arguments.
the problem is like below when construct private static final DBHelper INSTANCE = new DBHelper();
how can I solve it?
public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
this.context=context;
}
public static DBHelper getInstance(){
return DBHelper.DBHelperHolder.INSTANCE;
}
private static class DBHelperHolder{
private static final DBHelper INSTANCE = new DBHelper();
}
You can use DCL pattert, for example:
public class DBHelper {
private static volatile DBHelper instance;
public static DBHelper getInstance(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
if (instance == null) {
synchronized (DBHelper .class) {
if (instance == null) {
instance = new DBHelper(context, name, factory, version);
}
}
}
return instance;
}
}
But you need to supply arguments every time you call getInstance.
In addition to #Bracadabra's answer,
It is recommended to add private constructor() as it is singleton pattern and we want to restrict new object creation of our class DBHelper
I prefer LOCK object for synchronisation (to be thread safe)
We should restrict clone() calls also, so that outside code can not clone this class by .clone() call. So, override clone() method and return CloneNotSupportedException() from it.
public class DBHelper {
private static DBHelper instance;
// LOCK object for synchronisation
private static Object LOCK = new Object();
// Private Constructor
private DBHelper() { }
public static DBHelper getInstance(Context context, String name,
SQLiteDatabase.CursorFactory factory, int version) {
if (instance == null) {
synchronized (LOCK) {
if (instance == null) {
instance = new DBHelper(context, name, factory, version);
}
}
}
return instance;
}
#Override
protected DBHelper clone() throws CloneNotSupportedException {
return CloneNotSupportedException();
}
Related
I have a class App which extends Application and has a static method which returns context(which is static).
public class App extends Application {
private static Context context;
#Override
public void onCreate() {
super.onCreate();
context = this.getApplicationContext();
DBHelper dbHelper = new DBHelper();
DatabaseManager.initializeInstance(dbHelper);
}
public static Context getContext() {
return context;
}
}
Now in the DBHelper class which extends SQLiteAssetHelper in the constructor, i have this:
public DBHelper() {
super(App.getContext(), DATABASE_NAME, null, DATABASE_VERSION);
}
This is the DataBaseManager class:
public class DatabaseManager {
private Integer mOpenCounter = 0;
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
}
public static synchronized DatabaseManager getInstance() {
if (instance == null) {
throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
" is not initialized, call initializeInstance(..) method first.");
}
return instance;
}
public synchronized SQLiteDatabase openDatabase() {
mOpenCounter+=1;
if(mOpenCounter == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
}
Everything works fine, but there is a design problem because context fields should not be static. How do I use a context in DBHelper while keeping the code working and the field non-static?
You can pass ApplicationContext inside the DBHelper constructor like below:
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
and then
DBHelper dbHelper = new DBHelper(getApplicationContext());
I have a fragment, which is being used inside a MainActivity, actually it is used inside a ViewPager in a MainActivity.
public class Myfragment extends Fragment implements MySingleton.ResponseInterface{
public static Myfragment newInstance() {
final Myfragment mf = new Myfragment();
return mf;
}
public Myfragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
//.....
final MySingleton mysingleton = MySingleton.getInstance(getContext());
//I have a button in the fragment that I use like this
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mysingleton.getSomeResponse();
}
});
}
#Override
public void onResponseGiven(String response) {
Log.d("response", response);
}
}
I have a Singleton class to be used for different actions, the singleton includes an interface:
public class MySingleton{
private static MySingleton mInstance;
private static Context mContext;
public ResponseInterface responseInterface;
private MySingleton(Context context){
mContext = context;
this.responseInterface = (ResponseInterface) context;
}
public static synchronized MySingleton getInstance(Context context){
if(mInstance == null){
mInstance = new MySingleton(context);
}
return mInstance;
}
public void getSomeResponse(){
responseInterface.onResponseGiven("send response");
}
public interface ResponseInterface{
void onResponseGiven(String response);
}
}
Why do I get ClassCastException telling that MainActivity cannot be casted to MySingleton.ResponseInterface??
In your MySingleton class
public class MySingleton{
private static MySingleton mInstance;
private static Context mContext;
public ResponseInterface responseInterface;
private MySingleton(Context context){
mContext = context;
}
public static synchronized MySingleton getInstance(Context context){
if(mInstance == null){
mInstance = new MySingleton(context);
}
return mInstance;
}
public void getSomeResponse(ResponseInterface responseInterface){
this.responseInterface = responseInterface;
responseInterface.onResponseGiven("send response");
}
public interface ResponseInterface{
void onResponseGiven(String response);
}
}
And in your Fragment
final MySingleton mysingleton = MySingleton.getInstance(getActivity());
// edited here
mysingleton.getSomeResponse(Myfragment.this);
You should pass this to the getInstance method:
MySingleton.getInstance(this);
And try to cast it to MySingleton.ResponseInterface instead of MySingleton.
If you are casting MainActivity to MySingleton.ResponseInterface, you have to be sure that is implementing it.
In you actual code, MyFragment is implementing MySingleton.ResponseInterface.
I guess MainActivity doesn't
When you are calling MySingleton.getResponse(), you are sending the message to the responseInterface in your singleton. (I guess your activity, given it is a context when you create your singleton's instance).
public void getSomeResponse(){
responseInterface.onResponseGiven("send response");
}
If you want to get the answer to your fragment, you have to ensure tha the singleton is calling to your fragment instance.
Anyway, there is a very serieus design issue. attaching a fragment/activity instance to your singleton can lead to memeory leaks if you don't free when them are destroyed. read this
Also your singleton isn't really a singleton, you create a new instance after you call getIntance every time.
You are getting class cast exception because in the line final MySingleton mysingleton = MySingleton.getInstance(getContext());, you are passing activity context and you are trying to cast that context to ResponseInterface. ResponseInterface is implemented by Myfragment.
To solve it: You can change MySingeton class constructor as below
private MySingleton(Context context, ResponseInterface responseInterface){
mContext = context;
this.responseInterface = responseInterface;
}
and the calling part (in MyFragment OnCreateView) as
final MySingleton mysingleton = MySingleton.getInstance(getContext(), this);
and in getInstance method as
public static synchronized MySingleton getInstance(Context context, ResponseInterface responseInterface){
if(mInstance == null){
mInstance = new MySingleton(context, responseInterface);
}
return mInstance;
}
I have 2 classes which connect to a database. But with my current implementation I have to connect to 2 different databases.How can I fix this? If I use the same name for both databases it gives me an error
One class.....
public static final String DATABASE_NAME = "140398L.db";
Context context;
public PersistantAccountDAO(Context context) {
super(context, DATABASE_NAME, null, 1);
this.context = context;
}
Other class......
public class PersistantTransactionDAO extends SQLiteOpenHelper implements TransactionDAO {
public static final String DATABASE_NAME = "140398L";
public PersistantTransactionDAO(Context context) {
super(context, DATABASE_NAME, null, 1);
}
The DatabaseName is different, one class is 140398L.db and the other is 140398L
I have database and i want fill it with some values from xml file.
I'm using this code stream = context.getResources().openRawResource(R.xml.test_entry); to define the stream, but context make error "cannot resolve symbol 'context'".
I tried replace context with getActivity(), getContext(), this, class name and it still doesn't work. I need some help...
public class DatabaseHelper extends SQLiteOpenHelper {
<...>
public DatabaseHelper(Context context) {
super(context, dbName, null, dbv);
}
public void onCreate (SQLiteDatabase db) {
<...>
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS "+tableName);
onCreate(db);
/////////////////
loadTestValuyes(); <----- My function
/////////////////
}
public void loadTestValuyes() {
test_addEntry stackOverflowXmlParser = new test_addEntry();
List<Entry> entries = null;
InputStream stream = null;
///// I need context here
stream = context.getResources().openRawResource(R.xml.test_entry);
/////////////////
try {
entries = stackOverflowXmlParser.parse(stream);
} finally {
if (stream != null) {
stream.close();
}
}
for (Entry entry : entries) {
<...>
}
}
}
Thanks
You're passing a Context as a constructor argument. Just store it to a member variable:
private Context mContext;
public DatabaseHelper(Context context) {
super(context, dbName, null, dbv);
mContext = context;
and then use mContext where you need a Context.
You can save a reference to the Context object as an instance variable and use it wherever you need it:
public class DatabaseHelper extends SQLiteOpenHelper {
private Context mContext;
public DatabaseHelper(Context context) {
super(context, dbName, null, dbv);
mContext = context;
}
}
I'm trying to pass params into a AsyncTaskLoader. How do I do that?
Currently, I'm putting what I need to pass in in a separate static class. Anyway around this?
public class NewsAsyncTaskLoader extends AsyncTaskLoader<List<Content>> {
private static final DbHelper dbHelper = DbHelperFactory.getDbHelper();
public FeedAsyncTaskLoader(Context context) {
super(context);
}
#Override
public List<Content> loadInBackground() {
List<Content> contents = DbHelper.getStream(FeedSections.getInstance().getCurrentSection());
feed.setContents(contents);
return feed;
}
}
Pass additional parameters into your constructor:
public FeedAsyncTaskLoader(Context context, String moreInfo) {
super(context);
// Do something with moreInfo
}