问题
I have an app where the user can create/delete/edit to a list from a DialogFragment. Before I had a method like this in my DialogFragments: MainActivtity.adapter.add(String name, String location, double price);
adapter
is my adapter object for the recyclerView. The class for the adapter had my create/delete/edit methods for the items in the recyclerView. Which was called like shown above which also is a horrible way to call mehtods as I understand.
So I choose to put all these CRUD methods in a one singleton class and call these method like this: Service.getInstance().add(...);
Is this a correct approach and what could I have done better?
This is how I made the singleton class that now contains my CRUD methods, instead of putting them in my adapter class for the recyclerView as before.
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();
}
This class is just used for getting the global/Application context
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;
}
}
Question updated from here:
Here is the new approach based on suggestions from comments: Now everytime I want to make a CRUD operation in the Realm Database, I always start with the getDefaultInstance for my realm object and finish of with realm.close(); this procces is reapted in every CRUD method.
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 configurations is now moved to my Application class as suggested by Realm.
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:
You should not be using realm with singleton at all. As realm is thread oriented. you should open new default instance and close it.
Or you can use a map of threadId vs realm object. where thread is weak reference.
回答2:
Realm
represents a thread-confined local instance that needs to be closed when the thread reaches end of execution (or the local instance is no longer needed).
Any objects retrieved from the Realm are bound to the Realm's lifecycle, meaning they are invalidated once every Realm instance is closed on the given thread (the call to getInstance()
increases a reference count, and close()
decreases it).
So creating a static Realm realm
will most likely be accessible only on the UI thread.
To use Realm as a singleton (and be able to create/retrieve thread-local Realm instance in a reliable manner), you'd need something like 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.");
}
}
}
That way, you can open/close Realm at the beginning/end of execution, but still obtain Realm without necessarily passing it as a method argument.
private void doSomething() {
Realm realm = realmManager.getLocalInstance();
Or
realmManager.openLocalInstance();
// .. do whatever
realmManager.closeLocalInstance();
来源:https://stackoverflow.com/questions/37768882/have-i-used-singleton-with-realm-database-correct