Is this a better way to write realm access variables? I am using static variable because I notice that I only need 1 declaration of database connection for all realm queries in other class. But I am curious if this is still good at coding standards.
using Realms;
using System;
using System.IO;
namespace RealmDatabase
{
public class RealmDBAccessVariable
{
public static readonly string dbPath =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal),
"default.realm");
public static readonly RealmConfiguration config = new RealmConfiguration(dbPath, true);
}
}
This is the singleton pattern that I use for a Realm instance that I use on the UI Thread:
Xamarin.Android:
public sealed class UIRealm
{
static Realms.Realm instance;
public UIRealm(Activity activity, RealmConfiguration config)
{
if (instance == null)
activity.RunOnUiThread(() =>
{
instance = Realms.Realm.GetInstance(config);
});
else
throw new Exception("A Realm instance has already be created");
}
public static Realms.Realm Instance
{
get
{
if (instance == null)
throw new Exception("Call new UIRealm(Activity, RealmConfiguration) first");
return instance;
}
}
}
Usage:
// Create a UIRealm() only once
new UIRealm(this, new RealmConfiguration("realm.db"));
~~~
var aState = new State();
aState.Name = "WA";
UIRealm.Instance.Manage(aState);
var washingtonState = UIRealm.Instance.All<State>().Where((state => state.Name == "WA"));
Xamarin.Forms:
public sealed class UIRealm
{
static Realm instance;
public UIRealm(RealmConfiguration config)
{
if (instance == null)
Device.BeginInvokeOnMainThread(() =>
{
instance = Realm.GetInstance(config);
});
else
throw new Exception("A Realm instance has already be created");
}
public static Realm Instance
{
get
{
if (instance == null)
throw new Exception("Call new UIRealm(RealmConfiguration) first");
return instance;
}
}
}
Usage:
// Create a UIRealm() only once
new UIRealm(new RealmConfiguration("realm.db"));
~~~
var aState = new State();
aState.Name = "WA";
UIRealm.Instance.Manage(aState);
var washingtonState = UIRealm.Instance.All<State>().Where((state => state.Name == "WA"));
I think you will most probably run into problems the moment you try to write to the Realm on a background thread, and you attempt to access the realm for this operation which you opened on the UI thread.
static is generally insufficient, ThreadLocal is what would achieve this in Java.
I'm also not sure why this object is a RealmObject.
Related
I'm using Realms as a database in Android app. Works fine, but I've added a new label in my user model and I'm getting the error that I need to migrate my schema:
java.lang.RuntimeException: Unable to create application com.apelucy.apelucy.app.base.MyApplication: io.realm.exceptions.RealmMigrationNeededException: Migration is required due to the following errors:
- Property 'User.testRealm' has been added.
How can I do the migration? I've found other solutions here but I can't implement them in my code. I can't use a solution of delete and install the app. I now that work in development, but I need to update the app in production.
My UserRespository class:
public class UserRepository {
private static UserRepository sInstance = null;
private Context mContext = null;
public static UserRepository getInstance(Context context) {
if (sInstance == null) {
sInstance = new UserRepository();
sInstance.mContext = context;
}
return sInstance;
}
// DATABASE Methods
public void storeUser(final User user) {
AppSingleton.getInstance().setUser(user);
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(realm1 -> realm1.insertOrUpdate(user));
} finally {
if (realm != null) {
realm.close();
}
}
}
public User retrieveUser() {
Realm realm = null;
User user = null;
try {
realm = Realm.getDefaultInstance();
User userRealmResult = realm.where(User.class)
.findFirst();
if (userRealmResult != null) {
user = realm.copyFromRealm(userRealmResult);
}
} finally {
if (realm != null) {
realm.close();
}
}
return user;
}
public void clearUser() {
// Clear Database objects
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(realm1 -> realm1.delete(User.class));
} finally {
if (realm != null) {
realm.close();
}
}
}
}
Init realm in my Application:
Realm.init(this);
My model change:
#SerializedName("test")
#Expose
private String testRealm;
Migrations allow you to modify the schema of the application, which means that it lets you add, remove, rename tables/fields in the Realm schema. If you change a RealmModel class, then you must write the migration that will map the existing Realm file to reflect the new model classes.
RealmConfiguration config = new RealmConfiguration.Builder()
.schemaVersion(1)
.migration(new MyMigration())
.build();
Realm.setDefaultConfiguration(config);
The default schema version is 0.
Migrations are fairly straightforward:
you must increment the schema version, so Realm knows you want to increment the schema's version to a specific number
you must supply a migration that will handle the change from one version to another
Migrations describe the operations to do when you need to go from one schema version to another:
public class MyMigration implements RealmMigration {
#Override
public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
// Migrate from version 0 to version 1
if (oldVersion == 0) {
RealmObjectSchema userSchema = schema.get("User");
userSchema.addField("testRealm", String.class);
oldVersion++;
}
if (oldVersion == 1) { // ...
// ...
}
}
#Override
public int hashCode() { return MyMigration.class.hashCode(); }
#Override
public boolean equals(Object object) { return object != null && object instanceof MyMigration; }
}
Add this in your Application file. This will Realm to delete everything if you add a new table to a column.
RealmConfiguration config = new RealmConfiguration.Builder().name("dbname.realm")
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(config);
I have an Android app with a Room database. In my UI test, the Room database should be re-initialized at the beginning.
The tables cannot simply be deleted inside of Room because the Room.databaseBuilder has a callback. The callback initializes data in the tables.
instance = Room.databaseBuilder(
context,
xxxDatabase.class,
xxxDatabaseHelper.DB_NAME)
.fallbackToDestructiveMigration()
.addCallback(new xxxCallback(context)) <-- initialize db data
.build();
When I drop all tables, I expect that Room thinks it's a new install and re-initializes the db. However, I get an exception that Room can't write to a read-only db.
#Before
public void setUp() {
context = InstrumentationRegistry.getContext();
targetContext.deleteDatabase(RoosterDatabaseHelper.DB_NAME);
}
Exception:
(1032) statement aborts at 78: [INSERT OR ABORT INTO `resource_type`(`id`,`name`,`precursor_resource_type_id`,`precursor_unit_type_id`) VALUES (?,?,?,?)] attempt to write a readonly database
04
How is the Room database reset for a UI test?
A better method I learned for testing database is to create a ServiceLocator class like this
public class ServiceLocators {
private static ServiceLocators instance;
private static xxDatabase sDatabaseClass;
private ServiceLocators() {
}
public static ServiceLocators getInstance() {
if (instance == null) {
instance = new ServiceLocators();
}
return instance;
}
public static xxDatabase getDatabaseClass(Context context) {
if (sDatabaseClass != null) {
return sDatabaseClass;
} else {
return createDatabase(context);
}
}
public static void setDatabaseClass(xxDatabase databaseClass) {
ServiceLocators.sDatabaseClass = databaseClass;
}
public static void resetRepository() {
sDatabaseClass.clearAllTables();
sDatabaseClass.close();
}
private static xxDatabase createDatabase(Context context) {
return Room.databaseBuilder(
context.getApplicationContext(),
xxDatabase.class,
"database_name.db"
).build();
}
}
then in your code, you call the db like this
ServiceLocators.getInstance().getDatabaseClass(context)
in your test classes, create setUp method in which you simply swap the implementation of the original database
#Before
public void setUp() throws Exception {
xxDatabase database = Room.inMemoryDatabaseBuilder(context, xxDatabase.class)
.allowMainThreadQueries()
.build();
ServiceLocators.setDatabaseClass(database);
}
and teardown method to clean up the database tables
#After
public void tearDown() throws Exception {
ServiceLocators.resetRepository();
}
Hi I have gone through the best practices in Realm and according to it the best way to handle Realm instances is to open a realm instance in onCreate() method of an Activity using relam = Realm.getDefaultInstance() and close it in onDestroy() using realm.close().
But currently I have following singleton structure in my code. I need to know the pros and cons of the following singleton structure over the best practice suggested in realm docs.
Here is my code: Approach 1
public class RealmManager {
private static final String TAG = "RealmManager";
private RealmAsyncTask transactionManager;
private static RealmManager mInstance = null;
public static RealmManager getInstance() {
if (mInstance == null)
mInstance = new RealmManager();
return mInstance;
}
private Realm mRealm;
protected RealmManager() {
mRealm = Realm.getDefaultInstance();
}
public void saveOrUpdateChatChannel(ChatChannel channel) {
mRealm = Realm.getDefaultInstance();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm bgRealm) {
bgRealm.copyToRealmOrUpdate(channel);
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
Log.e(TAG,"Failed to update Channel");
}
});
}
public void deleteChatChannel(String channelID, OnRealmDatabaseListener mRealmListener) {
mRealm = Realm.getDefaultInstance();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
RealmResults<ChatChannel> result = realm.where(ChatChannel.class).equalTo("channelId", channelID).findAll();
result.deleteAllFromRealm();
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
if (mRealmListener != null)
mRealmListener.isDatabaseOperationSuccess(channelID, true);
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(#NonNull Throwable error) {
}
});
}
public void closeRealm() {
if (mRealm != null) {
mRealm.close();
}
if (transactionManager != null) {
transactionManager.cancel();
}
}
}
So in the Approach 1, I will be creating realm instances in my activities, services, intent services using RealmManager.getInstance() and then continue to do transactions. And in all my Activity onDestroy() methods i am closing the realm using RealmManager.closeRealm(). So my question is, if the RealmManager.closeRealm() which is called in Activity onDestroy(), will affect any transactions which are executing in Service?
Here is my code: Approach 2
public class RealmManager {
private static RealmManager mInstance = null;
public static RealmManager getInstance() {
if (mInstance == null)
mInstance = new RealmManager();
return mInstance;
}
private Realm mRealm;
protected RealmManager(){
mRealm = Realm.getDefaultInstance();
}
public void addClockModel(ClockRLM clockRLM,OnRealmDatabaseListener mRealmListener){
RealmAsyncTask transactionManager = mRealm.executeTransactionAsync(realm -> realm.copyToRealm(clockRLM), new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.d("Clocke ", "Inserted TimeStamp " + clockRLM.getTimeStamp());
if (mRealmListener != null)
mRealmListener.isDatabaseOperationSuccess(clockRLM,true);
if (transactionManager != null)
transactionManager.cancel();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
if (transactionManager != null)
transactionManager.cancel();
}
});
}
}
So in the Approach 2, I will be creating realm instances in my activities, services, intent services using RealmManager.getInstance() and then continue to do transactions. I am not sure where to close realm if i use Approach 2. What if i do not close it anywhere and only when the app closes, the RealmManager gets destroyed and realm instance will be destroyed. OR i need to close the realm instance in the application level (I am not sure whether we can close instance in the application level).
Which one is better among Approach 1 and Approach 2. Or is it better to open a realm instance in onCreate() method of an Activity using relam = Realm.getDefaultInstance() and close it in onDestroy() using realm.close().
Realm is hard to use under a "singleton manager" because Realm.getDefaultInstance() might seem like you're getting something that's a "singleton", but it's really not. Instances of Realm are thread-local and reference-counted, each call to getInstance() increments a ref count while close() decrements it.
I've said a few times that open() would have been a better name, but I came to this conclusion far too late :)
First, the reason why your Singleton approach is not good is because:
Calls to the methods can only be done from the thread that first calls RealmManager.getInstance(), which is expected to the UI thread (but not guaranteed)
You hard-code that you want to execute each 1 operation in 1 async transaction, so you can't use this thing on a background thread too much
In order to make a realm manager that can be invoked on any threads, and UI thread uses async transaction while background thread uses sync transaction, you'd need to use a method like this.
And also, you'd need to track the open Realm instance for that given thread, so that you can access it wherever you want, without incrementing the ref count.
public class RealmManager {
private final ThreadLocal<Realm> localRealm = new ThreadLocal<>();
public Realm openLocalInstance() {
Realm realm = Realm.getDefaultInstance();
if(localRealm.get() == null) {
localRealm.set(realm);
}
return realm;
}
public Realm getLocalInstance() {
Realm realm = localRealm.get();
if(realm == null) {
throw new IllegalStateException("No open Realms were found on this thread.");
}
return realm;
}
public void closeLocalInstance() {
Realm realm = localRealm.get();
if(realm == null) {
throw new IllegalStateException(
"Cannot close a Realm that is not open.");
}
realm.close();
if(Realm.getLocalInstanceCount(Realm.getDefaultConfiguration()) <= 0) {
localRealm.set(null);
}
}
With a class like this, you could do:
try {
realmManager.openLocalInstance();
// Realm is open and can be accessed on this thread with `realmManager.getLocalInstance()`
// also all your methods in the RealmManager can use `getLocalInstance()`
} finally {
realmManager.closeLocalInstance();
}
I also created a library a while ago that wraps Realm in such a way that it eliminates the need for manual reference counting, but it never really caught on. Check out the source if curious.
I'm trying to use notify() and wait(). Here is my desired class.
I have a problem, when I try to call addNewItem(). If I call tryToReadItem() first and then call addNewItem() method, that log will not be printed. Note that my DemoClass is the singleton.
public class DemoClass {
private static final String TAG = "DemoClass";
private static DemoClass instance;
private Object lock = new Object();
private static Thread executor;
private static Runnable reader;
static MyQueue queue;
private DemoClass() {
queue = MyQueue.getInstance();
reader = new Runnable() {
#Override
public void run() {
tryToReadRequest();
}
};
}
public static DemoClass getInstance() {
if (null == instance) {
instance = new RequestExecutor();
executor = new Thread(reader);
executor.run();
}
return instance;
}
public boolean addNewItem() {
synchronized (lock) {
lock.notify(); // executor will be run
Log.i(TAG, "executor run...");
}
return true;
}
public void tryToReadItem() {
try {
while (true) {
synchronized (lock) {
if (queue.checkTopValue() == null) {
Log.v(TAG, "queue is empty");
lock.wait();
} else {
//TODO other code...
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Here is the usage of that class:
DemoClass executor = DemoClass.getInstance();
boolean bool = executor.addNewItem();
Am I missing something?
Edit: I just changed my code. Now tryToReadRequest() is executed continuously while queue is not empty. but my problem is that the line lock.notify(); does not execute.
There are many problems with this code
First of all
if (queue.checkTopValue() == null) {
Log.v(TAG, "queue is empty");
lock.wait();
}
Depends on official documentation
Note: Always invoke wait inside a loop that tests for the condition
being waited for. Don't assume that the interrupt was for the
particular condition you were waiting for, or that the condition is
still true.
Your DemoClass is Singleton. But not thread safe Singleton
because multiple threads can pass null == instance condition at the same time
if (null == instance) {
instance = new RequestExecutor();
executor = new Thread(reader);
executor.run();
}
Right way is additional check in synchronized block and using volatile instance.
so add volatile to instance
private static volatile DemoClass instance;
and rewrite getInstance() method to something like this
public static DemoClass getInstance() {
DemoClass localInstance = instance;
if (localInstance == null) {
synchronized (DemoClass.class) {
localInstance = instance;
if (localInstance == null) {
localInstance = new DemoClass();
instance = localInstance;
executor = new Thread(reader);
executor.run();
}
}
}
return localInstance;
}
note, you can leave only check inside synchronized block, but that will make getInstance method too slow.
I have 6236 rows with Arabic character I used predefined database and load it successfully here is the code for read the file
private String copyBundledRealmFile(InputStream inputStream, String outFileName) {
try {
File file = new File(this.getFilesDir(), outFileName);
FileOutputStream outputStream = new FileOutputStream(file);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, bytesRead);
}
outputStream.close();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
and make configure for realm here
copyBundledRealmFile(SplashScreen.this.getResources().openRawResource(R.raw.tests), "test");
RealmConfiguration config1 = new RealmConfiguration.Builder(SplashScreen.this)
.name("test")
.schemaVersion(1)
.migration(new Migration())
.build();
Realm.setDefaultConfiguration(config1);
and make check to read and copy realm one time
but problem loading the data take about 5 seconds every time the app open to make the configure and have instance of realm
here is the code of realm instance
private static MyReleam instance;
private final Realm realm;
public MyReleam(Application application) {
realm = Realm.getDefaultInstance();
}
public static MyReleam with(Fragment fragment) {
if (instance == null) {
instance = new MyReleam(fragment.getActivity().getApplication());
}
return instance;
}
public static MyReleam with(Activity activity) {
if (instance == null) {
instance = new MyReleam(activity.getApplication());
}
return instance;
}
public static MyReleam with(Application application) {
if (instance == null) {
instance = new MyReleam(application);
}
return instance;
}
public Realm getRealm() {
return realm;
}
and use it here
this.realm = MyReleam.with(this).getRealm();
how can I optimize using it and decrease time of loading
1.) Use initialData() or assetFile() (preferably assetFile()) instead of populating in a migration
2.) Forget everything you've read in this tutorial because it's an outdated mess (I can tell that's where the MyReleam is from) and refer to my article instead
thanks for answer of EpicPandaForce I use documentation as https://realm.io/docs/java/latest/api/io/realm/RealmConfiguration.Builder.html
the I was conflict in asset pass it was only the name of files in asset
RealmConfiguration config1 = new RealmConfiguration.Builder(SplashScreen.this).assetFile(SplashScreen.this,"tests")//name of files in assets (test)
.name("test")
.schemaVersion(1).migration(new Migration())
.build();
Realm.setDefaultConfiguration(config1);