Singleton返回两个实例

时间:2015-08-19 13:24:12

标签: java android android-fragments singleton

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

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

RecentFragment:

Recent_Fragment

HistoryFragment:

History_Frag

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

此外:

  • mAppContext @ 4093:相同
  • photoStorage @ 4094:相同

当我从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;
    }
}

}

1 个答案:

答案 0 :(得分:0)

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

  

Singleton设计模式解决了所有这些问题。使用Singleton设计模式,您可以:
  确保只创建一个类的一个实例
  提供对象的全局访问点

在您的情况下,我们看不到PhotoStorage类,但此调用来自实例,Singletton pattern不允许的内容:

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

此行有效,但由于get方法static不是Karakuri指向的良好做法,因此也违反了Singletton pattern定义。

public static PhotoStorage get(Context c){

<强>解
要使photoStorage.get()无效并创建正确的Singletton pattern,您必须:

  • 在类中声明getInstance()方法静态(此处为PhotoStorage
  • 隐藏默认构造函数以避免类的实例
  • 必要时创建私人构造函数
  • 以静态方式致电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;
    }
}

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

@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());
            }
        }
    }
}