Realm object not accesible when realm is closed. Android - android

I'm using Realm for Android.
I have issue (not really a big problem), I have this lines of code:
Account account;
....
realm = Realm.getDefaultInstance();
account = realm.where(Account.class).findFirst();
realm.close();
if (account.getJid().equals(mUser.getText().toString())) { // User is the same as logged before
launchLogin(mUser.getText().toString().split("#")[0],mPassword.getText().toString());
}
If I launch the app, when the execution arrives to IF statement, it crash because account object does'nt exist. Even when exist accounts in the db.
But If move the realm.close() inside the IF, after the launchLogin(..), it works .
What I understand is that account "dissapears" when I close the realm db. and I can get real problem in a future.
So I want to know how can I made "persistent" this type of problem. I mean, close realm after queries and the object still exist after it.

In addition to EpicPandaForces answer, if you really want to close the realm and discard any auto-update advantages Realm offers, you can create an unmanaged copy of the RealmObject using realm.copyFromRealm(realmObject);

Managed RealmObject instances can only be accessed from Realm instances that are not closed.
Following the official documentation, you should have an open instance for the UI thread bound to the lifecycle of the application itself.
From the docs:
// onCreate()/onDestroy() overlap when switching between activities so onCreate()
// on Activity 2 will be called before onDestroy() on Activity 1.
public class MyActivity extends Activity {
private Realm realm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
}
#Override
protected void onDestroy() {
super.onDestroy();
realm.close();
}
}
And
// Use onCreateView()/onDestroyView() for Fragments as onDestroy() might not be called.
public class MyFragment extends Fragment {
private Realm realm;
#Override
public void onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
realm = Realm.getDefaultInstance();
View view = inflater.inflate(R.layout.my_fragment, parent, false);
return view;
}
#Override
public void onDestroyView() {
super.onDestroyView();
realm.close();
}
}
And for background threads:
// Run a non-Looper thread with a Realm instance.
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
// ... Use the Realm instance ...
} finally {
if (realm != null) {
realm.close();
}
}
}
});
thread.start();
Following the docs, the problem you mention won't occur.

You're supposed to open the Realm instance at the beginning of the background thread, close it at the end of execution in that background thread,
try {
realm = Realm.getDefaultInstance();
account = realm.where(Account.class).findFirst();
if (account.getJid().equals(mUser.getText().toString())) { // User is the same as logged before
launchLogin(mUser.getText().toString().split("#")[0],mPassword.getText().toString());
}
} finally {
if(realm != null) {
realm.close(); // important
}
}

Related

What happens if I close my android application without closing an open realm instance?

I have kept a single realm instance opened on main thread in Application class and I use that single instance to do all kinds of DB operations from MainActivity. Since my application has a single activity, I close the instance in the activity's onDestroy(). The app is working fine for me as of now.
What are the repercussions of not doing a realm.close()? My database hasn't corrupted with or without the same.
Also, I've read that there are scenarios in which the Activity's onDestroy() may not get called at all. What effects the database can have in such a scenario if closing realm is so important?
public class MyApp extends Application {
private static MyApp instance;
private Realm realm;
public void onCreate() {
super.onCreate();
Realm.init(this);
Realm.setDefaultConfiguration(new RealmConfiguration.Builder()
.schemaVersion(BuildConfig.VERSION_CODE)
.migration(new RealmMigrationClass())
.compactOnLaunch()
.build());
realm = Realm.getInstance(Realm.getDefaultConfiguration());
}
public static MyApp getInstance() {
return instance;
}
public Realm getRealm() {
return realm;
}
}
MainActivity
public class MainActivity extends Activity {
#Override
protected void onDestroy() {
MyApp.getInstance().getRealm().close();
super.onDestroy();
}
}
Closing the realm instance is very important because of realm core has been written in c++ programming language and is compiled in the native code.And we know the c++ garbage collection does not run automatically we require to manually call the garbage collection.So when you call the realm.close() it means that realm deallocation the native memory means free or delete the pointer variable and also do the file descriptor job.From realm.close() means you give the command or tell to native c++ compiler to run the garbage collection.
If you look the "doc" (REALM_DOC) for Realm for Java you can find:
Realm implements Closeable to take care of native memory deallocation
and file descriptors, so always close your Realm instances when you’re
done with them.
Realm instances are reference counted—if you call getInstance twice in
a thread, you need to call close twice as well. This allows you to
implement Runnable classes without having to worry about which thread
will execute them: simply start it with getInstance and end it with
close.
Personally I suggest you to define a class in which define your Realm functions and an "Realm attribute" (like a "RealmHelper" class) then inside this class define:
- a unstatic Realm
- a static RealmHelper instance
You will always use this RealmHelper static instance for all operations in your Realm inside your main Thread, inside other threads you will call "new RealmHelper()" and CLOSE the realm just after you did the operation.
Doing this in your MainThread you just need to close ONE realm instance when the application get closed, to do this you can use the "Application.ActivityLifecycleCallbacks" interface inside a Custom defined Application class (so which extends Application of Android).
Example inside you Application custom class:
/* START Override ActivityLifecycleCallbacks Methods */
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
#Override
public void onActivityStarted(Activity activity) {
// Check if your MyRealmClass instance is null or is closed, in this case
// re-initialize it.
if(MyRealmClass.getInstance() == null || MyRealmClass.getInstance().getRealm().isClosed()){
MyRealmClass.initInstance();
}
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
if(!AppUtils.isAppOnForeground(this)){
// Close your MyRealmClass instance
if(MyRealmClass.getInstance() != null) {
MyRealmClass.getInstance().close();
MyRealmClass.getInstance().logRealmInstanceCount(LABEL_APP_IN_BACKGROUND);
MyRealmClass.setMyInstance(null);
}
}
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
/* END Override ActivityLifecycleCallbacks Methods */
Code of "isAppOnForeground" (check if your app is in foreground, if is not this mean your app is being closed):
public static boolean isAppOnForeground(Context context) {
boolean ret = false;
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if(appProcesses != null){
String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
ret = true;
}
}
}
return ret;
}
Your "MyRealmClass" will look like:
public class MyRealmClass {
protected Realm mRealm;
protected static MyRealmClass mInstance;
public MyRealmClass() {
mRealm = Realm.getDefaultInstance();
}
public static MyRealmClass initInstance(){
if(mInstance == null){
mInstance = new MyRealmClass();
}
return mInstance;
}
public static MyRealmClass getInstance(){
return mInstance;
}
public static void setMyInstance(MyRealmClass instance) {
mInstance = instance;
}
public Realm getRealm() {
return mRealm;
}
public void setRealm(Realm realm){
this.mRealm = realm;
}
public void close() {
if (mRealm != null) {
try {
mRealm.close();
} catch(Exception e){
onException(e);
}
}
}
[...]
Then you need to check that all your Realm instance is not closed when you use a RealmObject or you do some operation in your Realm. And if it is closed (because the app got in background and then restarted) you need to re-initialize the realm (if you have an activity with a MyRealmClass instance as attribute).
Example in a BaseMyActivity:
public abstract class MyBaseActivity extends AppCompatActivity {
protected MyRealmClass mRealmClass;
/* START Override Lifecycle Methods */
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initMyRealmClass();
Lyra.instance().restoreState(this, savedInstanceState);
}
#Override
protected void onStart() {
super.onStart();
initMyRealmClass();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Lyra.instance().saveState(this, outState);
}
/* END Override Lifecycle Methods */
/* START Private Methods */
protected void initMyRealmClass(){
if(mRealmClass == null || mRealmClass.getRealm().isClosed()){
mRealmClass = MyRealmClass.initInstance();
}
}
/* END Private Methods */
}
Basically all your activities will extend this BaseActivity if they need to use Realm functions. (Lyra is used to save the state of any of your attributes: LYRA)
REMEMBER THAT:
if you set or get some attributes from a RealmObject or you get an object from a RealmList or RealmResults you NEED THAT YOUR REALM INSTANCE, from which the object was take, IS OPEN.
OTHERWISE you need to use this method when you init a variable with objects from the realm: (this methods should be placed in yuour "MyRealmClass")
public <T extends RealmObject> List<T> toList(RealmResults<T> results) {
return mRealm.copyFromRealm(results);
}
public <T extends RealmObject> List<T> toList(RealmList<T> results) {
return mRealm.copyFromRealm(results);
}
public <T extends RealmObject> T copyObjectFromRealm(T obj) {
return mRealm.copyFromRealm(obj);
}
public <T extends RealmObject> RealmResults<T> findAllObject(Class<T> classObject) {
RealmQuery<T> query = mRealm.where(classObject);
return query.findAll();
}
Now if you need to get a List of "MyRealmObjectClass" objects and add them to an adapter you will do this:
List<MyRealmObjectClass> myObjects = mRealmClass.toList(mRealmClass.findAllObject(MyRealmObjectClass.class))
myAdapter.addAll(myObjects);
Doing this if you "get" or "set" an attribute after the Realm instance, from which you got the objects, was closed (for example after the app get to background and then restarted) you won't get an exception.
BUT if you "set" an attribute of your RealmObject this WON'T BE SET in the REALM INSTANCE, so to change the value of a RealmObject inside the Realm in this case you need to Save the object!
OTHERWISE if you have a RealmResults or a RealmObject which is still connected to the Realm, so you can directly change, inside a transaction, an attribute of it and it will be changed inside the Realm too.
To do a Realm Transaction I suggest you to follow the DOC in the first link and, if you don't need to close the Realm in the Finally block, enable lambda and do this:
mRealm.executeTransaction(
realm -> {
[do your Realm operations]
}
)
or you can also do:
public boolean doRealmOperation(Object... params){
AtomicBoolean ret = new AtomicBoolean(false);
mRealm.executeTransaction(
realm -> {
try{
[do your realm operation]
ret.set(true);
} catch(Exception e){
onException(e)
ret.set(false);
}
}
)
}
in this case you need to use the "AtomicBoolean" because you will set the value you want to return inside the transaction, but inside a transaction the value got from outside of the transaction itself (in this case the "ret" variable) MUST BE A FINAL variable. But you can't define "ret" as "final" and then set it again, so you need to use the "AtomicBoolean" to set the variable outside the transaction and set it again inside the transaction itself.
(You can also avoid this problem by using a temporary variable to get the "true/false" value inside the transaction and then set the "ret" variable using that "temp variable". But personally I prefer to use "AtomicBoolean" class which is, I think, safer and more clean than a temp variable)
Hope this is helpful,
see you by and happy coding! ;)
Realm implements Closeable to take care of native memory deallocation and file descriptors, so always close your Realm instances when you’re done with them.
Realm instances are reference counted—if you call getInstance twice in a thread, you need to call close twice as well.
From my personal experience not closing realm has not caused a lot of issues, in fact when I tried closing it at times it would cause an issue when the app went into the background and was then resumed which caused a crash due to realm instance being closed, I am not sure why a new instance of realm was not created in that case, might have been a bug.
As of now I follow the realm docs and close my realm instances until they cause an issue.
General coding practises suggest that anything that is opened should be safely closed.
Yes, it will get closed only if you called close() method on your application's destroy() method. Remember Realm implements Closeable in order to take care of native memory deallocation and file descriptors so it is important to close your Realm instances when you are done with them.
For further info visit this link.

How can you access managed objects created by different Realm instances?

I have created an application in Android that uses realm.
I have a splash screen that loads all the data needed to operate, and a number of activities that use that data.
That data is stored in an Application class extension as static variables.
My problem is that when I try to manipulate the data in other activities I get an error about the realm database being closed.
I have solved this by creating a static reference to my database on Application. Opening it in OnCreate() and closing it in OnTerminate() but I get the feeling that this is wrong.
I have also solved it by creating unmanaged objects (i.e. after retrieving them from Realm using copyFromRealm) and then when I need to do alterations copying them back into a realm instance I just created.
What is the correct way to solve this problem?
I have a splash screen that loads all the data needed to operate, and a number of activities that use that data.
That data is stored in an Application class extension as static variables.
That's very interesting - have you tried to put the application in background on a screen, then go to Android Studio's Logcat tab, go to the additional menu items in the bottom left, and click TERMINATE?
Fun fact is that if your application is in background when you do this, and then you re-open the app from the launcher, you'll have the exact same effect as if your app had been killed via low memory condition.
In your case, you'll just crash with an NPE on pretty much any screen that is not the splash screen! Very, very interesting.
(Also, Application.onTerminate() is never called. So that's not helpful either.)
Anyways, to get back to your original question,
My problem is that when I try to manipulate the data in other activities I get an error about the realm database being closed.
What is the correct way to solve this problem?
The section Best Practices: Controlling the lifecycle of Realm instances section of the official documentation is fairly helpful in this regard.
public class MyActivity extends Activity {
private Realm realm;
private RealmResults<Dog> dogs;
private RealmChangeListener<RealmResults<Dog>> realmChangeListener = (results) -> {
// do something on first load + future changes of Dogs
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
setContentView(R.layout.activity_main);
dogs = realm.where(Dog.class).sort("age").findAllAsync();
dogs.addChangeListener(realmChangeListener);
}
#Override
protected void onDestroy() {
super.onDestroy();
dogs.removeAllChangeListeners();
dogs = null;
realm.close();
realm = null;
}
}
or
public class MyFragment extends Fragment {
private Realm realm;
private RealmResults<Dog> dogs;
private RealmChangeListener<RealmResults<Dog>> realmChangeListener = (results) -> {
// do something on first load + future changes of Dogs
};
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
realm = Realm.getDefaultInstance();
View root = inflater.inflate(R.layout.fragment_view, container, false);
return root;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
dogs = realm.where(Dog.class).sort("age").findAllAsync();
dogs.addChangeListener(realmChangeListener);
}
#Override
public void onDestroyView() {
super.onDestroyView();
dogs.removeAllChangeListeners();
dogs = null;
realm.close();
realm = null;
}
}
Realm instances are ref-counted on a given thraead, so I guess the answer is, "keep one open while you need it". And of course, close it when you no longer do.

Realm access from incorrect thread

I have an application with a LoginActivity, that when the user login correctly, I register to receive messages. And the LoginActivity jumps to MainActivity.
The arriving messages are supposed to be stored in database (Realm), to recover from a Realm instance in Main.
But when the message arrives It crash realm launching this errror:
Exception in packet listener
java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
at io.realm.BaseRealm.checkIfValid(BaseRealm.java:383)
at io.realm.Realm.executeTransactionAsync(Realm.java:1324)
at io.realm.Realm.executeTransactionAsync(Realm.java:1276)
at es.in2.in2tant.LoginActivity.newMessageReceived(LoginActivity.java:124)
at es.in2.in2tant.Connection.Connection$4$1.processMessage(Connection.java:227)
at org.jivesoftware.smack.chat.Chat.deliver(Chat.java:180)
at org.jivesoftware.smack.chat.ChatManager.deliverMessage(ChatManager.java:351)
at org.jivesoftware.smack.chat.ChatManager.access$300(ChatManager.java:53)
at org.jivesoftware.smack.chat.ChatManager$2.processPacket(ChatManager.java:162)
at org.jivesoftware.smack.AbstractXMPPConnection$4.run(AbstractXMPPConnection.java:1126)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
I'm a bit lost on how Realm works, and I don't know how to make realm accessible across the application without a crash and keep storing this received messages from LoginActivity. Some help, or approaches to achieving this?
LoginActivity.java:
public class LoginActivity extends AppCompatActivity implements ConnectionConnectResponse {
.....
protected void onCreate(Bundle savedInstanceState) {
//Realm Init config:
Realm.init(this);
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().build();
Realm.deleteRealm(realmConfiguration); // Clean slate
Realm.setDefaultConfiguration(realmConfiguration); // Make this Realm the default
#Override
public void newMessageReceived(final ChatMessage message) {
Logger.d("NEWMESSAGERECEIVED :" + message);
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Message receivedMessage = realm.createObject(Message.class, message.id);
receivedMessage.setBodyMessage(message.message);
receivedMessage.setFrom(message.from);
receivedMessage.setTo(message.to);
receivedMessage.setDelivered(false);
receivedMessage.setMine(false);
receivedMessage.setDate(Calendar.getInstance().getTime());
}
});
//Logger.d("NEWMESSRE: LAST MESSAGE:" + realm.where(Message.class).equalTo("chatID", message.id));
}
#Override
protected void onStart() {
super.onStart();
realm = Realm.getDefaultInstance();
}
#Override
protected void onStop() {
super.onStop();
realm.close();
}
Image of what is needed:
Realm access from incorrect thread. Realm objects can only be accessed
on the thread they were created.
This error message is quite self-explanatory.
As i see you're initializing realm by calling Realm.getDefaultInstance() on the UI thread.
The error is coming from newMessageReceived(), so i guess that method is called from a background thread.
Either obtain a Realm instance on the background thread and use that instead of the global instance:
#Override
public void run () {
Realm backgroundRealm = Realm.getDefaultInstance();
backgroundRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Message receivedMessage = realm.createObject(Message.class, message.id);
receivedMessage.setBodyMessage(message.message);
receivedMessage.setFrom(message.from);
receivedMessage.setTo(message.to);
receivedMessage.setDelivered(false);
receivedMessage.setMine(false);
receivedMessage.setDate(Calendar.getInstance().getTime());
}
});
}
Or, if you would like to stick to the global Realm instance for some reason, then make sure your code is executed on the UI thread by calling runOnUiThread() (or directly posting a Runnable to the message queue of the main thread through a Handler):
#Override
public void newMessageReceived(final ChatMessage message) {
runOnUiThread(new Runnable() {
#Override
public void run() {
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Message receivedMessage = realm.createObject(Message.class,
message.id);
receivedMessage.setBodyMessage(message.message);
receivedMessage.setFrom(message.from);
receivedMessage.setTo(message.to);
receivedMessage.setDelivered(false);
receivedMessage.setMine(false);
receivedMessage.setDate(Calendar.getInstance().getTime());
}
});
}
});
}
Just create Realm backgroundRealm = Realm.getDefaultInstance() each time you want to access database and don't forget to close it using realm.close()
Allocate instance before transaction and release it right after transaction is complete, so you won't have linegring connection and by doing so, you perform savings from thread scheduler.
I use 'Data Access Object' interface for manipulations with data and that interface is implemented using Realm. Don't use 'transaction async', use all calls synchronously, but perform calls on 'data access object' from background thread. Good solution for that - rxJava library.
Just get and release Realm instance all the time - library makes it inexpensive.
I haven't use Realm yet. But what i understood from the error message is that ConnectionConnectResponse may be alive when Loginactivity die. So you should pass Realm instance as a parameter inside
newMessageReceived(Realm realm, final ChatMessage message)
and put all the Realm init code in the class where you fire this method.

Android Realm how to check if Realm has been initialized

I've a problem with the initialization of Realm.
I get systematically an error with :
Realm realminstance = Realm.getDefaultInstance();
I caught this exception :
No default RealmConfiguration was found. Call setDefaultConfiguration() first
I know I have to init Realm before using it, but could you tell me how can I check if Realm is initialized ?
It doesn't works with:
if(Realm.getDefaultConfiguration == null){...}
Thank you very much.
This code snippet here should work: https://realm.io/docs/java/latest/#controlling-the-lifecycle-of-realm-instances
// Setup Realm in your Application
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this).build();
Realm.setDefaultConfiguration(realmConfiguration);
}
}
public class MyActivity extends Activity {
private Realm realm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
}
#Override
protected void onDestroy() {
super.onDestroy();
realm.close();
}
}
You could use a try catch block. I initially initialised my realm in the application class and used it in a content provider and sync adapter but the provider is created before the application apparently so I was forced to do a try catch in all 3 to avoid whatever race condition may occur (couldn't find documentation for what gets created first amongst the components).
`try{
realm = Realm.getDefaultInstance();
} catch (Exception e){
Realm.init()
//Configuration, schema, migration, and encryption details come here
//Now we get the default instance: again.
realm = Realm.getDefaultInstance();
}`

Using a Singleton for Realm in UI-Thread

I use Realm in a Project. I built this RealmActivity to easily use Realm in code with getActivity.getRealm().
public abstract class RealmActivity extends AppCompatActivity {
private Realm realm;
#Override
protected void onCreate(Bundle savedInstanceState) {
realm = Realm.getDefaultInstance();
super.onCreate(savedInstanceState);
}
#Override
protected void onDestroy() {
super.onDestroy();
if (!realm.isClosed()) {
realm.close();
}
}
public Realm getRealm() {
return realm;
}
Now I want to delete the Realm-file to clear the data for this session/user. To do this, all RealmInstances have to be closed. I tried to start a Logout-Activity with an intent to clear the activityStack. It works fine for my Nexus 5 (Android 6) but not for my Nexus 4 (Android 5).
My idea was to create a Singleton for the RealmInstance, which used in the UI-Thread
public abstract class RealmActivity extends AppCompatActivity {
private Realm realm;
#Override
protected void onCreate(Bundle savedInstanceState) {
realm = ManagerLayer.getInstance().getRealm();
super.onCreate(savedInstanceState);
}
#Override
protected void onDestroy() {
super.onDestroy();
}
public Realm getRealm() {
return realm;
}
}
Now, I got only one Instance and can close it before deleting the file. But I never close the RealmInstance when I´m not logging out. Is that an Problem?
But when I clear the ActivityStack and the onDestroy() method isn´t called, the RealmInstance for the Activity gets never closed, too. Right?
Can I use a Singleton for the UI-Thread realmInstance? I never pass it to another Thread and never close it.
Or is there another solution to close all RealmInstances to delete the RealmFile?
If you want to keep the instance of some object that is needed for the whole application, you should keep this instance in:
The Application class, extending it and setting the android:name in the AndroidManifest.xml.
Another class that is not an Android component, a simple java class.
Talking about lifecycles, if you have some action that closes the Realm instance and this is enough, simply create a clear method in your singleton and call it properly.
If this is not the case, you have to keep in mind that you can never warranty that the onDestroy method is called, but if you don't release the resources because the process has been killed, you don't have to worry at all, since when a process is killed all the objects not persisted will be completely released, even if they are using the filesystem or the network.

Categories

Resources