我是否正确地使用了Realm数据库中的Singleton?

8
我有一个应用程序,用户可以通过DialogFragment创建/删除/编辑列表。以前,在我的DialogFragments中有这样一个方法:MainActivtity.adapter.add(String name, String location, double price); adapter 是我用于recyclerView的适配器对象。适配器类中包含了recyclerView中项目的创建/删除/编辑方法。如上所示的调用方式非常糟糕。
因此,我选择将所有CRUD方法放在一个单例类中,并像下面这样调用这些方法:Service.getInstance().add(...); 这是一种正确的方法吗?我还能做得更好吗?
这是我创建的单例类,它现在包含了我的CRUD方法,而不是像以前一样将它们放在recyclerView的适配器类中。
public class Service {

private static Realm realm;
private static Service service = new Service();

private Service() {
    realm = Realm.getInstance(App.getAppContex());
}

public static Service getInstance(){

    if(service == null){
        service = new Service();
    }
    return service;
}



   public void add(String name, String location, double price) {


    ShopListItem shopListItem = new ShopListItem();

    shopListItem.setName(name);
    shopListItem.setLocation(location);
    shopListItem.setPrice(price);
    shopListItem.setTimestamp(System.currentTimeMillis());
    shopListItem.setIsBought(0);

    realm.beginTransaction();
    realm.copyToRealm(shopListItem);
    realm.commitTransaction();
}


public void removeItem(int position, List<ShopListItem> shopListItems) {

    realm.beginTransaction();
    shopListItems.remove(position);
    realm.commitTransaction();
}

这个类仅用于获取全局/应用程序上下文

public class App extends Application {


public static Application sApplication;

public static Application getsApplication(){
    return sApplication;
}

public static Context getAppContex(){

    return getsApplication();
}

@Override
public void onCreate() {
    super.onCreate();
    sApplication = this;
}
}

从这里开始更新问题:

以下是根据评论提出的建议采用的新方法: 现在,每次我想要在 Realm 数据库中执行 CRUD 操作时,我总是从我的 realm 对象中获取 getDefaultInstance 并以 realm.close() 结束;这个过程在每个 CRUD 方法中都会重复。

public class Service {

private Realm realm;
private static Service service = new Service();

private Service() {


}

public static Service getInstance(){

    if(service == null){
        service = new Service();
    }
    return service;
}

public void removeItem(int position, List<ShopListItem> shopListItems) {
    //new: realm = Realm.getDefaultInstance();

    realm = Realm.getDefaultInstance();
    realm.beginTransaction();
    shopListItems.remove(position);
    realm.commitTransaction();
    realm.close();
    //new: realm.close();
}

按照 Realm 的建议,Realm 配置现在已经移至我的 Application 类中。

public class App extends Application {


public static Application sApplication;

public static Application getsApplication(){
    return sApplication;
}

public static Context getAppContex(){

    return getsApplication();
}

@Override
public void onCreate() {
    super.onCreate();
    sApplication = this;
final RealmConfiguration realmConfiguration = new    RealmConfiguration.Builder(App.getAppContex()).build();
    realm.setDefaultConfiguration(realmConfiguration);
}
}

1
单例模式通常是危险的,特别是当您从不同的线程访问单例时。请参阅文档中的这两个部分,了解有关如何处理 Realm 生命周期的更多信息:https://realm.io/docs/java/latest/#controlling-the-lifecycle-of-realm-instances 和 https://realm.io/docs/java/latest/#closing-realm-instances - Christian Melchior
在我的 onDestroy 方法中有 @TimCastelijns。 - user5366495
1
不是必须的,但如果您想使所有方法都线程安全,应该在方法开头打开Realm,并在退出时再次关闭它。请注意,只有在实例处于打开状态时才可以访问Realm数据,因此仅当您不返回任何数据时才能使用此方法。 - Christian Melchior
现在没有理由将Realm作为数据成员,它也不应该是静态成员。将其作为局部变量,这样就可以保证线程安全了。 - user207421
什么?最佳实践?不存在的。你还没有把它变成局部变量,因此它仍然不是线程安全的。 - user207421
显示剩余7条评论
2个回答

2

您不应该将Realm和单例一起使用。因为Realm是面向线程的。您应该打开新的默认实例并关闭它。

或者您可以使用线程ID与Realm对象的映射表,其中线程是弱引用。


我将Service类设为单例的原因是,这样我就可以在我的应用程序/代码的任何地方调用CRUD方法,这样我就不必为每个需要这个方法的类编写一个add()方法。 - user5366495
@Muddz 那么在一个静态类中创建add/remove方法吗? - egfconnor
在Java中创建单例模式的最佳方法不再是使用设计模式。您可以使用Dagger 2来创建单例,这样更具可重用性、可测试性和可修改性。 - Sainik

1

Realm代表一个线程限定的本地实例,在线程执行结束时(或者不再需要本地实例时)需要关闭。

从Realm检索到的任何对象都绑定到Realm的生命周期,这意味着一旦给定线程上的每个Realm实例关闭(调用getInstance()会增加引用计数,而close()会减少它),它们就无效了。

因此,创建一个static Realm realm很可能只能在UI线程上访问。

要将Realm用作单例(并能够以可靠的方式创建/检索线程本地的Realm实例),您需要类似于this的东西:

@Singleton
public class RealmManager {
    private final ThreadLocal<Realm> localRealms = new ThreadLocal<>();

    @Inject
    RealmManager() {
    }

    /**
     * Opens a reference-counted local Realm instance.
     *
     * @return the open Realm instance
     */
    public Realm openLocalInstance() {
        checkDefaultConfiguration();
        Realm realm = Realm.getDefaultInstance();
        if(localRealms.get() == null) {
            localRealms.set(realm);
        }
        return realm;
    }

    /**
     * Returns the local Realm instance without adding to the reference count.
     *
     * @return the local Realm instance
     * @throws IllegalStateException when no Realm is open
     */
    public Realm getLocalInstance() {
        Realm realm = localRealms.get();
        if(realm == null) {
            throw new IllegalStateException(
                    "No open Realms were found on this thread.");
        }
        return realm;
    }

    /**
     * Closes local Realm instance, decrementing the reference count.
     *
     * @throws IllegalStateException if there is no open Realm.
     */
    public void closeLocalInstance() {
        checkDefaultConfiguration();
        Realm realm = localRealms.get();
        if(realm == null) {
            throw new IllegalStateException(
                    "Cannot close a Realm that is not open.");
        }
        realm.close();
        // noinspection ConstantConditions
        if(Realm.getLocalInstanceCount(Realm.getDefaultConfiguration()) <= 0) {
            localRealms.set(null);
        }
    }

    private void checkDefaultConfiguration() {
        if(Realm.getDefaultConfiguration() == null) {
            throw new IllegalStateException("No default configuration is set.");
        }
    }
}

那样,您可以在执行开始/结束时打开/关闭Realm,但仍然可以获取Realm而无需将其作为方法参数传递。
private void doSomething() {
    Realm realm = realmManager.getLocalInstance();

或者

    realmManager.openLocalInstance();
    // .. do whatever
    realmManager.closeLocalInstance();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接