单例返回两个实例

Singleton returning two instances

我正在尝试使用单例 (PhotoStorage) 来提供 Photo 对象的 arrayList,但 PhotoStorage 实例的行为似乎不像单例(两个实例)。

我正在使用 dagger 将这个单例注入一个名为 class 的 PhotoInteractor。到目前为止,objectGraph 似乎 A-OK。 同一个 PhotoInteractor 实例用于 viewpager 中的三个片段。这些片段都是在运行时实例化的:

最近片段:

Recent_Fragment

历史片段:

History_Frag

注意两个片段的 PhotoInteractor 实例 @4067 是如何相同的。

还有:

当我从 RecentFragment 单击照片对象(网格图像)时,会调用 PhotoStorage.addPhoto(url) 方法。这会正确地将照片对象添加到 photoStorage 实例数组 (4094)。这么多就好了。

问题:

当我关闭应用程序时,PhotoStorage.savePhotosToFile 方法将此 arrayList 对象序列化为文件系统上的 JSON。

从同一个 PhotoInteractor 实例调用以下方法:

@Override
public void savePhotos(){
    photoStorage.get(mAppContext).savePhotosToFile();
}

当我调试应用程序时,PhotoStorage.get 方法已经有一个单例实例,但似乎是第二个实例!

//Singleton enforcement
public static PhotoStorage get(Context c){
    if(sPhotoStorage == null){
        sPhotoStorage = new PhotoStorage(c.getApplicationContext());
    }
    return sPhotoStorage;
}

这意味着照片 ArrayList 将始终为空,因为它是 PhotoStorage 的新实例。我不确定它从哪里实例化自己。

编辑 - 添加 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;
    }
}

}

您没有以正确的方式执行 Singletton pattern

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

在你的情况下,我们没有看到 PhotoStorage class 但这个调用来自一个实例,这是 Singletton pattern 不允许的:

photoStorage.get(mAppContext).savePhotosToFile();
//↑ instance call WRONG!!

这条线有效,但由于您的 get 方法是 static,这不是一个好习惯,因为 Karakuri 指出并打破了 Singletton pattern定义。

public static PhotoStorage get(Context c){

解决方案
要使 photoStorage.get() 无效并创建正确的 Singletton pattern,您必须:

  • 在 class(此处 PhotoStorage)中声明 getInstance() 静态方法
  • 隐藏默认构造函数以避免 class
  • 的实例
  • 必要时创建私有构造函数
  • 以静态方式调用getInstance()它:

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;
    }
}

然后你可以从class范围允许的任何地方进行静态调用:

@Override
public void savePhotos(){
      PhotoStorage.get(mAppContext).savePhotosToFile();
    //↑ static call CORRECT!!
}

更新:
如果您的应用有多个线程并且单例 getInstance 请求可能会重叠,则有一个 double check syncronized singletton pattern 您可以申请:

//Singleton enforcement
public synchronized static PhotoStorage get(Context c){
    if(sPhotoStorage == null){
        synchronized(PhotoStorage.class) {
            if(sPhotoStorage == null) {
                sPhotoStorage = new PhotoStorage(c.getApplicationContext());
            }
        }
    }
}