Keep application state on android - android

I am having trouble saving the state/singleton of my application.
When the application starts a loading screen (activity) is shown and a singleton is initialized with values from a webservice call (note that network access cannot run on the main thread).
After the singleton is created I open my main activity. Note that values from the singleton are required to build the layout.
Now assume the app goes in the background and is killed there (e.g. because of low memory). My singleton instance is deleted as the app is killed. When I switch back to my app it tries to recreate the main activity. As I mentioned earlier the values from the singleton are required to build the layout, so this leads to a NullPointerException (when I try to access members of the singleton, as it is not there anymore).
Can I somehow tell android to start the first loading activity after the app was killed? It would be great if I could refresh the singleton before the layout is recreated, but this seems to be a problem as network calls can not be on the main thread and therefore not block until the refresh is finished.
I assume that I could save the singleton in all activities onStop and recreate it in the onCreate methods, but this seems a bit too unpredictable and would probably lead to a inconsistent state...
Another way could be to just always finish my activity onStop, but this would lead to losing on which tab the user last and so on, even if the app is not killed, so this is not a good option.
Any ideas on how to solve this?

Why not just use a SharedPreferences instead of a singleton?
Anytime you want to save some global state, commit it to preferences. Anytime you want to read the global state, read it back from preferences.
Then you don't have to concern yourself with application lifecycle at all, as your data will always be preserved regardless of what the phone is doing.

For something like that I used a pseudo singelton object as a Application class. This object will be created on the beginning and will be in the memory. But note that the system will terminate the application if the memory is needed by other applications. However this object is persitent even if all activities are temporally terminated.
To use that you need to declare that in your android manifest like here:
<application android:label="#string/app_name"
android:icon="#drawable/icon"
android:description="#string/desc"
android:name=".MySingeltonClass"
...
Here is a code example:
public abstract class MySingeltonClass extends Application {
// ...
public void informClientOnline() {
clientOnline=true;
Log.v(LOG_TAG, "Client is online!");
}
public void informClientShutdown() {
clientOnline=false;
Log.v(LOG_TAG, "Client is going offline. Waiting for restart...");
Timer t=new Timer("shutdowntimer", false);
t.schedule(new TimerTask() {
#Override
public void run() {
if(!clientOnline) {
Log.v(LOG_TAG, "Client has not restartet! Shutting down framework.");
shutdown();
System.exit(0);
}
}
}, 5000);
}
}
this two functions are called like this:
((MySingeltonClass)getApplicationContext()).informClientOnline();

You could save your Singleton when onSaveInstanceState() in the Activity gets called. All you need to do is to make it implement Parcelable (it's Androids own form of serialization), then you can put it in the outState Bundle in onSaveInstanceState() which will allow you to retrieve it laver in onCreate() or onRestoreInstanceState() in the Activity, whichever you like.
I've included an example for you:
public class TestActivity extends Activity {
private MySingleton singleton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState.containsKey("singleton")) {
singleton = savedInstanceState.getParcelable("singleton");
} else {
singleton = MySingleton.getInstance(5);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("singleton", singleton);
}
public static class MySingleton implements Parcelable {
private static MySingleton instance;
private int myData;
private MySingleton(int data) {
myData = data;
}
public static MySingleton getInstance(int initdata) {
if(instance == null) {
instance = new MySingleton(initdata);
}
return instance;
}
public static final Parcelable.Creator<MySingleton> CREATOR = new Creator<TestActivity.MySingleton>() {
#Override
public MySingleton[] newArray(int size) {
return new MySingleton[size];
}
#Override
public MySingleton createFromParcel(Parcel source) {
return new MySingleton(source.readInt());
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(myData);
}
}
}

Related

Memory leak even if I release retained objects in onDestroy?

I know that this is a bad idea to pass context/activity to another object that may live longer then activity and store it, but in my app I really need this. Here is a pseudocode that I'm using
class MyActivity extends Activity implements ActivityDelegate {
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MySingleton.getShared().setDelegate(this);
}
void onDestroy() {
super.onDestroy();
MySingleton.getShared().setDelegate(null);
}
}
class MySingleton{
private static MySingleton shared;
private ActivityDelegate delegate = null;
public static MySingleton getShared() {
if (shared == null)
shared = new MySingleton();
return shared;
}
void setDelegate(ActivityDelegate delegate) {
this.delegate = delegate;
}
}
As you can see I always set delegate to null in onDestroy, this is why memory leak should not occur. But as far as I know there are some cases when onDestroy is not called. I guess this only happens when the entire app process is purged and the memory get released anyway. So, my question is - is this safe to pass context/activity to another object and then unset it in onDestroy call or it can still cause leaks in some cases ? Thanks

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.

Singleton Destruction, Class Loading etc in android

I want to get clarity on loading of classes, destruction of objects etc in android because I noticed some weird things happening when using Singleton in My Activity. Best I will describe it using code :
My Singleton class
public class FilterCriteria {
private final String TAG=FilterCriteria.class.getSimpleName();
private static FilterCriteria filterCriteria=new FilterCriteria();
private FilterCriteria()
{
}
public static FilterCriteria getInstance()
{
return filterCriteria;
}
private int rentUpperBound,rentLowerBound;
private int bedrooms,baths;
private float distance;
private ObjectStateListener listener;
public void setFilters(float distance,int baths,int bedrooms,int rentLowerBound,int rentUpperBound) {
this.distance = distance;
this.baths=baths;
this.bedrooms=bedrooms;
this.rentLowerBound=rentLowerBound;
this.rentUpperBound=rentUpperBound;
if(listener!=null)
listener.onObjectStateChanged();
}
public void attachListener(ObjectStateListener listener) {
if (this.listener == null) {
this.listener = listener;
Log.v(TAG, "NO LISTENER PRESENT AS EXPECTED");
} else {
Log.v(TAG, "LISTENER PRESENT!!! BUT THE ACTIVITY WAS STARTED JUST NOW.");
}
}
public void destroy()
{
filterCriteria=null;
}
}
The attachListener(ObjectStateListener listener) function is called only once in the activity. So, when I open my activity the first time, I get this log from attachListener function
NO LISTENER PRESENT AS EXPECTED
Now, I close the activity and then reopen it. But now I get this log
"LISTENER PRESENT!!! BUT THE ACTIVITY WAS STARTED JUST NOW."
So, that means the object still lives on even after the activity (and the application) was closed. Is this normal???
So, I tried to destroy the singleton instance using the destroy() function in the onDestroy() function of Activity.
#Override
protected void onDestroy(){
filterCriteria.destroy();//Trying to destroy the singleton
super.onDestroy();
Log.v(TAG,"Destroying activity");
}
But I got NullPointerException on this line filterCriteria.destroy(). So, that means android has already made object null, whereas when I see in debug mode, other members of the Activity are still alive. Why is only this null?
What is happening!???
When you invoke the method attachListener() you are creating a reference to the linked object (even if it is static): this reference will be binded to the activity lifecycle.
On the other hand, filterCriteria will follow the static field Java-like lifecycle (but you can still remove this reference manually).

Android database Application context fails

This article shows the different database approaches nicely:
database approaches
I use approach 2: An Application object which holds the single LocalDatabaseAdapter I made. This holds a DatabaseOpenHelper etc.
public class MyApplication extends Application {
private static LocalDbAdapter lDb;
public void onCreate() {
super.onCreate();
MyApplication.context = getApplicationContext();
lbm = LocalBroadcastManager.getInstance(context);
[..]
}
public static LocalDbAdapter getLDb(){
if(lDb==null){
lDb = new LocalDbAdapter(context);
}
if(lDb.isOpen()){
return lDb;
}else{
return lDb.open();
}
}
#Override
public void onTerminate() {
super.onTerminate();
lDb.close();
}
Now when I start the camera app and then return to my main activity, I get the database never closed error. As it points out, this db was created in the Application context, so why is it a problem that I dont close it in my Activity, I thought that was the idea. An important adavantage of having only one database object is that all methods of LocalDbAdapter that do any writing use the protected(this) statement around the insert or update.
In OnActivityResult, after the camera has taken a picture, I acquire the database like
LocalDbAdapter ldb = MyApplication.getLDb();
This has me seriously bummed out. If I close it in onPause of my MainActivity, I'm afraid that a background service using the same object gets in trouble, and Im even keeping the object local to the onActivityResult...

Can I have a multi-process global variable?

I have a real problem using my app that involve 2 processes. One process its executing a Service (p1) and the other the GUI (p2).
I have a class in p2 that implements the use of an object (iThing) that is custom memory managed (and its static). It has to be like this bacause of Android OS implementation of destroying the views whenever he wants.
public class Connections{
public static int iGlobalCounter=0;
public static Object iThing;
public static void start(){
iGlobalCounter++;
Log.d("PROCESS", "UP: "+iGlobalCounter);
if (iGlobalCounter<=1){
//Create the object "iThing"
}
}
public static int stop(){
iGlobalCounter--;
Log.d("PROCESS", "DOWN: "+iGlobalCounter);
if (iGlobalCounter<=0){
//Destroy the object "iThing"
}
}
}
The main GUI (in p2), starts and stops the variable on the onCreate / onDestroy (for all views in my app)
public class MyMainClass extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Connections.start();
}
#Override
public void onDestroy(){
super.onDestroy();
Connections.stop();
}
}
Finally in p1 I have the service, which also needs the variable, so, it does the same
public class MyMainService extends Service{
#Override
public void onCreate() {
super.onCreate();
Connections.start();
}
#Override
public void onDestroy(){
super.onDestroy();
Connections.stop();
}
}
The problem is that if I use only p2 (GUI), it goes all well, but when I execute the service (in p1), the counter doesn't increment from the last state, but from 0, resulting in destroying the object when leaving the service, not the app.
if do this navigation, I get the following counters:
MyMainClass (1) --> OtherClass (2) --> AnotherClass (3) --> MyMainService (1)
My question is if there is a way of having a multi-process global variable? As it seems that every process takes its own static variables and are not "real static". A solution could be using SharedPreferences to save the state, but not really nice solution, as it hasn't to be saved when leaving the app.
Thanks,
PAU
I think that you should extend Application class and put your globalVariable there.
You can store your global data in shared memory (see MemoryFile).
To synchronize access to the file, I think the best approach is to implement some sort of spinlock using the same memory file.
In and case, I don't know a simply way of doing this.
You have the following options which you can look into for sharing data between different processes,
Message Queue,
Named Pipes,
Memory mapped files
WCF on Named Pipes or MSMQ

Categories

Resources