I'm trying to use a singleton (PhotoStorage) to provide an arrayList of Photo objects, but it seems that the PhotoStorage instance is not behaving as a singleton (two instances).
I am using dagger to inject this singleton into a class named PhotoInteractor. The objectGraph seems A-OK up to this point.
The same PhotoInteractor instance is used in three fragments in a viewpager. These fragments are all instantiated at runtime:
RecentFragment:
HistoryFragment:
Notice how the instance #4067 of the PhotoInteractor is the same for both fragments.
Also:
mAppContext#4093: same
photoStorage#4094: same
When I click a photo object (grid image) from RecentFragment, the PhotoStorage.addPhoto(url) method is called. This correctly adds the photo object to the photoStorage instance array (4094). That much is OK.
Problem:
When I close the applicaton, it is intended that the PhotoStorage.savePhotosToFile method serialzes this arrayList object into JSON on the filesystem.
The following method is called from the same PhotoInteractor instance:
#Override
public void savePhotos(){
photoStorage.get(mAppContext).savePhotosToFile();
}
When I debug the application, the PhotoStorage.get method already has a singleton instance, but what appears to be a 2nd instance!
//Singleton enforcement
public static PhotoStorage get(Context c){
if(sPhotoStorage == null){
sPhotoStorage = new PhotoStorage(c.getApplicationContext());
}
return sPhotoStorage;
}
This means that the ArrayList of photos will always be empty since it is a new instance of PhotoStorage. Iām not sure where it is instantiating itself from.
Edit - Added PhotoStorage.class:
public class PhotoStorage{
private ArrayList<Photo> mPhotos;
private PhotoJSONer mSerializer;
private static PhotoStorage sPhotoStorage;
private static Context mAppContext;
private static final String PHOTOS_DATABASE = "photos.json";
public static final String TAG = PhotoStorage.class.getSimpleName();
public PhotoStorage(Context appContext){
mSerializer = new PhotoJSONer(appContext, PHOTOS_DATABASE);
try{
mPhotos = mSerializer.loadPhotos();
}catch(Exception e){
mPhotos = new ArrayList<Photo>();
}
}
//Singleton enforcement
public static PhotoStorage get(Context c){
if(sPhotoStorage == null){
sPhotoStorage = new PhotoStorage(c.getApplicationContext());
}
return sPhotoStorage;
}
public ArrayList<Photo> getPhotos(){
return mPhotos;
}
public Photo getPhoto(String url){
for(Photo p: mPhotos){
if(p.getUrl() == url)
return p;
}
return null;
}
public void deletePhoto(String url){
Log.i(TAG, "deleted photo");
mPhotos.remove(url);
}
public void addPhoto(Photo photo){
Log.i(TAG, "added photo");
mPhotos.add(photo);
}
public boolean savePhotosToFile(){
try{
mSerializer.savePhotos(mPhotos);
return true;
}catch (Exception e){
return false;
}
}
}
You are not executing Singletton pattern in the correct way,
The Singleton design pattern addresses all of these concerns. With the Singleton design pattern you can:
Ensure that only one instance of a class is created
Provide a global point of access to the object
In your case, we don't see PhotoStorage class but this call comes from an instance, what is not allowed by Singletton pattern:
photoStorage.get(mAppContext).savePhotosToFile();
//ā instance call WRONG!!
This line works, but as your get method is static is not a good practice as Karakuri pointed and also breaks the Singletton pattern definition.
public static PhotoStorage get(Context c){
SOLUTION
To make photoStorage.get() invalid and create a correct Singletton pattern you must:
declare the getInstance() method static in the class (here PhotoStorage)
hide default constructor to avoid instances of the class
create private constructors if necessary
call getInstance() it in a static way:
class PhotoStorage {
// hidding default constructor
private PhotoStorage () {};
// creating your own constructor but private!!!
private PhotoStorage(Context appContext){
mSerializer = new PhotoJSONer(appContext, PHOTOS_DATABASE);
try{
mPhotos = mSerializer.loadPhotos();
}catch(Exception e){
mPhotos = new ArrayList<Photo>();
}
}
//Singleton enforcement
public synchronized static PhotoStorage get(Context c){
if(sPhotoStorage == null){
sPhotoStorage = new PhotoStorage(c.getApplicationContext());
}
return sPhotoStorage;
}
}
Then you can make static call from everywhere the class scope allows:
#Override
public void savePhotos(){
PhotoStorage.get(mAppContext).savePhotosToFile();
//ā static call CORRECT!!
}
UPDATE: if your app have several threads and singleton getInstance requests may overlapp, there is a double check syncronized singletton pattern you can apply:
//Singleton enforcement
public synchronized static PhotoStorage get(Context c){
if(sPhotoStorage == null){
synchronized(PhotoStorage.class) {
if(sPhotoStorage == null) {
sPhotoStorage = new PhotoStorage(c.getApplicationContext());
}
}
}
}
Related
Forgive the elementary question, I am learning Java still so need some advice on best practice here. I have a valid scenario where I wish to share the same object between two distinct Test classes using JUnit or TestNG. I understand that tests/test classes should not usually share state but this is a long-running journey.
I understand the JVM executes for both frameworks in this order:
#BeforeClass
Construcor call
#Before
#Test
Given I have an Person class with one field name and one getter & setter for same and I instantiate an instance of it in one Test Class:
public class FirstPersonTest {
private Person firstPerson;
#BeforeClass
private void setup() {
firstPerson = new Person("Dave");
}
#Test
public void testName() {
assertEquals("Dave", firstPerson.getName());
}
}
And a second Test class:
public class SecondPersonTest {
private Person firstPerson;
private static String name;
#BeforeClass
private void setup(){
name = firstPerson.getName(); //null pointer, firstPerson reference no longer exists from FirstPersonTest
}
#Test
public void testName(){
assertEquals("Dave", name);
}
}
What is the optimal way of accessing the firstPerson object in the second class? I don't want to instantiate it a second time because I wish to share state for a journey test.
I want to be able to pass firstPerson instance in the constructor or an annotated setup method, but don't wish to instantiate the SecondPersonTest within the body of FirstPersonTest
You can use a singleton class for this purpose.
public class LocalStorage {
private static volatile LocalStorage instance;
private Map<String, Object> data = new HashMap<>();
private LocalStorage() {
}
public static LocalStorage getInstance() {
if (instance == null) {
synchronized (LocalStorage.class) {
if (instance == null) {
instance = new LocalStorage();
}
}
}
return instance;
}
public static void addData(String key, Object value) {
getInstance().data.put(key, value);
}
public static Object getData(String key) {
return getInstance().data.get(key);
}
public static <T> T getData(String key, Class<T> clazz) {
return clazz.cast(getInstance().data.get(key));
}
}
You can store the whole Person object or only the name field of the Person object.
To store:
Person firstPerson = new Person("Dave");
LocalStorage.addData("Dave", firstPerson);
To get it back:
Person firstPerson = LocalStorage.getData("Dave", Person.class);
I am using an ArrayList in my application.
I would like to know the exact procedure to initialize my ArrayList from a Singleton class.
The data will be used in some other Activities.
Can anybody help to know about Singleton class?
Here is how to create your singleton class :
public class YourSingleton {
private static YourSingleton mInstance;
private ArrayList<String> list = null;
public static YourSingleton getInstance() {
if(mInstance == null)
mInstance = new YourSingleton();
return mInstance;
}
private YourSingleton() {
list = new ArrayList<String>();
}
// retrieve array from anywhere
public ArrayList<String> getArray() {
return this.list;
}
//Add element to array
public void addToArray(String value) {
list.add(value);
}
}
Anywhere you need to call your arrayList just do :
YourSingleton.getInstance().getArray();
To add elements to array use :
YourSingleton.getInstance().addToArray("first value");
or
YourSingleton.getInstance().getArray().add("any value");
Please look at the following wikipedia-artikle:
https://en.wikipedia.org/wiki/Singleton_pattern
But keep in mind that singletons are 'global state' and make your sourcecode harder to test. There are lot of people saying: "singletons are evil'
I think you need something like this.
public class SingletonClass {
private static ArrayList<String> strArray;
private static SingletonClass singleton;
private SingletonClass(){}
public static synchronized SingletonClass getConnectionInstance(ArrayList<String> strArray){
if (singleton == null) {
singleton = new SingletonClass();
}
this.strArray = strArray;
return singleton;
}
}
That's how I do it (android code)
private volatile static WifiManager wm;
private static WifiManager wm(Context ctx) {
WifiManager result = wm;
if (result == null) {
synchronized (WifiMonitor.class) { // the enclosing class
result = wm;
if (result == null) {
result = wm = (WifiManager) ctx
.getSystemService(Context.WIFI_SERVICE);
if (result == null) throw new WmNotAvailableException();
}
}
}
return result;
}
Bloch in Effective Java recommends :
// Lazy initialization holder class idiom for static fields
private static class FieldHolder {
static final FieldType field = computeFieldValue();
}
static FieldType getField() {
return FieldHolder.field;
}
This has the advantages of dispensing with the synchronization and that the JVM optimizes out the field access. The problem is that I need a Context object in my case. So :
Is there a way to adapt Bloch's pattern to my case (computeFieldValue() needs a param) ?
If not, is there a way to execute around ? The code is subtle and I'd rather have it in one place and pass only required behavior in
Last but not least - is there a way to enforce access to the cache field (wm) only via the wm() method ? So I can avoid NPEs and such. Other languages use properties for this
You can do the following
static int param1;
static String param2;
public static void params(int param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
private static class FieldHolder {
static final FieldType field = new FieldType(param1, param2);
}
static FieldType getField() {
return FieldHolder.field;
}
Note: even simpler than this is to use an enum,
enum Singleton {
INSTANCE;
}
or
class SingletonParams {
static X param1;
}
enum Singleton implements MyInterface {
INSTANCE(SingletonParams.param1, ...);
}
I've a simple class which perform some check agains list of Regex patterns. My patterns are set somehow with constant list of values. This is not a question as for now, so I'm omitting that setting code.
class Checker {
private final Pattern [] patterns = new Pattern[10];
public boolean check(final String param){
for(Pattern p : patterns){
if(p.matcher(param).matches())
return true;
}
return false;
}
}
Stuff is failry simple.
Now I'm accessing that class from 500 simultaenous threads. Class is created with default Spring scope singleton.
Making access to patterns synchronous:
class Checker {
private final Pattern [] patterns = new Pattern[10];
public boolean check(final String param){
synchronized(patterns){
for(Pattern p : patterns){
if(p.matcher(param).matches())
return true;
}
return false;
}
}
}
Well, since all threads access single instance of my class - they gets locked within synchronized block. Since my class is fairly lightweight I'd like to create separate instance for each thread (e.g. change scope from singleton to prototype). Removing synchronization block and adding getInstance factory method backed by ThreadLocal. Like that:
class Checker {
private final Pattern [] patterns = new Pattern[10];
public boolean check(final String param){
for(Pattern p : patterns){
if(p.matcher(param).matches())
return true;
}
return false;
}
private static final ThreadLocal<Checker> checkerLocal =
new ThreadLocal<Checker>() {
#Override
protected Checker initialValue() {
System.out.println("Creating Checker instance")
return new Checker();
}
};
public static Checker getInstance(){
return checkerLocal.get();
}
}
Works like a charm.
However, imagine that I've to dynamically modify patterns somehow inside my application (e.g. add or remove Pattern there). I should perform that for all existing instances of Checker class.
I'm thinking on using ApplicationContext#getBeansOfType(Checker.class) method to get all bean instances. But is there any other more springy way to do so?
Here's my suggestion. The Checker class doesn't have to have any synchronization at all, since it only ever pulls an unmodifiable List<Pattern> out of the holder class, and the only synchronization needed is to prevent clashing updates on the holder.
public class PatternService {
AtomicReference<List<Pattern>> patterns = new AtomicReference<>(Collections.emptyList());
public List<Pattern> getPatterns() {
return patterns.get();
}
public synchronized void addPattern(Pattern p) {
List<Pattern> newPatterns = new LinkedList<>(patterns.get());
newPatterns.add(p);
patterns.set(Collections.unmodifiableList(newPatterns)); // or ImmutableList
}
// other mutators
}
public class Checker {
#Autowired PatternService patternService;
public boolean check(String param) {
for(Pattern p: patternService.getPatterns())
if(p.matcher(param).matches())
return true;
return false;
}
}
I have an Android app which keeps a persistent log of events in a custom object that extends ArrayList. It is kept in a singleton so that various activities all point to the same one log. The main activity reloads the list in the onResume.
Problem is, when I reload the log, all the UI elements (like the ArrayAdapter) lose the reference and I need to set them up again. It works. But, it seems like a lot of work.
How can I reload the object into the original object's instance so that I don't have to do all that setup again? Using a singleton should make it easier not harder.
I know it's all a problem with pass-by-reference. But I just can't get my head around it.
Java is not my first language... Thanks.
Activity onCreate:
dataLog = DataLog.getInstance();
logView = (ListView) findViewById(R.id.logView);
dataLogAdapter = new ArrayAdapter<String>(this,R.layout.log_row, dataLog);
logView.setAdapter(dataLogAdapter);
Activity onResume:
dataLog = Persister.recoverLog(this, "datalog.dat");
dataLogAdapter = new ArrayAdapter<String>(this,R.layout.log_row, dataLog);
logView.setAdapter(dataLogAdapter);
Persister:
public static DataLog recoverLog(Context context, String fileName){
File file = context.getFileStreamPath(fileName);
DataLog obj = new DataLog ();
if(file.exists() && file.length()>0){
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(file);
ois = new ObjectInputStream(fis);
obj = (DataLog) ois.readObject();
ois.close();
fis.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
return obj;
}
DataLog:
public class DataLog extends ArrayList<String> implements Serializable {
private static final long serialVersionUID = 0L;
public DataLog() {}
private static DataLog _instance;
public static DataLog getInstance() {
if(_instance==null){
_instance = new DataLog();
}
return _instance;
}
public boolean add(String entry) {
super.add(entry);
return true;
}
public void add(int index, String entry) {
if (index > 0)
super.add(index, entry);
else
super.add(entry);
}
public void clear() {
super.clear();
}
}
Your datalog isn't a true singleton. If it were, no new reference would be created after it was created the very first time.
This:
dataLog = Persister.recoverLog(this, "datalog.dat");
should not return a Datalog but some internal data DataLog uses (an ArrayList of Strings) and should be called within DataLog
Then only pass the dataLog instance around. If you ever reassign it you'll have problems. When you reload data log you'll need to reload the structures relying on DataLog's array instance.
Handle reloading your DataLog data in onResume if necessary, but do it like so:
DataLog.getInstance().load();
So that your data log is never reassigned. That's the key. DataLog is a wrapper around your data. If DataLog's internal state is changed, no references to DataLog are lost.
Here's an example:
public class DataLog implements Serializable {
//internalize this data.
private ArrayList<String> data;
private static final long serialVersionUID = 0L;
//singleton's constructor should be private.
private DataLog() {}
private static DataLog _instance;
public static DataLog getInstance() {
if(_instance==null){
_instance = new DataLog();
}
return _instance;
}
public ArrayList<String> getData(){
//copy this if you're worried about it being modified.
//DO NOT pass this reference around. Pass a reference to data log,
//reload GUI relying on this reference as needed.
return data;
}
public void load(){
data = //reload string array from disk.
}
public boolean add(String entry) {
super.add(entry);
return true;
}
public void add(int index, String entry) {
if (index > 0)
data.add(index,entry);
else
data.add(entry);
}
public void clear() {
data.clear();
}
}