Singletons in Android activity and service lifecycles - android

I am trying to use a static singleton DataManager that makes network requests and holds arrays of data for my Activity, instantiated in my Activity's onCreate() class, but the idea that the Activity gets destroyed every time there is an orientation change is tripping me up. I don't want to re-create a new singleton and re-populate it with data every time the user changes the orientation or comes back to the screen.
Even if I make DataManager a Service, if I make it a Bound Service, it seems like the Service will get destroyed whenever my Activity gets destroyed, but if I don't make it a bound service and use startService() and stopService() in my Activity, it also gets destroyed whenever my Activity is destroyed.
Also, if I use onSaveInstanceState() and onRestoreInstanceState() to save my instance of the singleton, it possible that my singleton would get destroyed when my Activity is inactive, since there is no longer a pointer to it. Then Activity B using the same DataManager class could create another DataManager instance while Activity A is inactive. Then Activity A wakes up, inflating another Data Manager, giving us 2 DataManagers that are no longer singletons and may have inconsistent data.
I have read elsewhere that I should not subclass Application to maintain app state, but I don't understand how it would work any other way. Thanks for any clarification.

Subclass the application class and then instantiation your singleton within the application.onCreate() callback. This way it will be available for the lifetime of your application rather than the lifetime of a single activity. Careful that this won't be garbage collected until someone kills your app so don't have too many "Global" singletons.

DO NOT do the work in Application.onCreate(). You will be slowing down your application start up, no matter what happens. This is not advised for Android applications, you want your app to start promptly.
Instead, if you really need a singleton, have it construct lazily when it's necessary.(If you are sure you will use it, you you can also force the construction asynchronously from a separate thread when your activity starts). When your activity gets destroyed, it doesn't mean the whole process will be immediately torn down, so your singleton will stay alive.
Also, if you are using a singleton, make sure to be able to clear it when memory is low. You will need to implement Application.onTrimMemory(int) and clear the singleton from there.

Related

Application lifecycle when app is killed in the background

Some background on a problem that I have been encountering: in my app I have a singleton object that I use regularly to access things like id and token for network calls. Sometimes when the app is killed in the background, this singleton loses its state. However, when the app is opened again and starts up in some Activity past the launcher Activity, the singleton is null.
I am in the process of refactoring this, but have been agonizing over how to ensure that the singleton will always be present even in app restart, but I'm not sure what Android does when the app restarts from being backgrounded.
I went through source code in some of the libraries we use in the app (Facebook, Intercom) to see how they manage their singletons and why their static variables seemed to just always be present, and came upon a theory.
So on a normal app cold start, the app behaves like this:
Application.onCreate() -> Launcher.onCreate() -> Activity A -> Activity B
Say the user was in Activity B and backgrounds the app. After using some other apps, they come back to my app but it has been killed at some point in between. The lifecycle then becomes this:
Application.onCreate() -> Activity B
I think the problem is that I initialize the singleton in the launcher Activity, and as a result, when B tries to get a value from the singleton, it comes up null. If I initialize the singleton in Application.onCreate(), it will always be initialized when the app is pulled up again. Is this correct?
TL;DR An application that is killed and brought to foreground again will always call its Application.onCreate() and then forward directly to the Activity it was on. Therefore, app initializations that are critical to the app functioning should live in the Application onCreate().
A application that is killed and brought to foreground again will always call its Application.onCreate() and then forward directly to the activity it was on. Therefore, app initializations that are critical to the app functioning should live in the Application onCreate().
Correct.
I think the problem is that I initialize the singleton in the LauncherActivity, and as a result, when B tries to get a value from the singleton, it comes up null. If I initialize the Singleton in Application.onCreate(), it will always be initialized when the app is pulled up again. Is this correct?
In your case the problem is really in that. However, if by "it comes up null" you mean the singleton instance is null then it is not how a singleton is supposed to work. No matter where you call singleton's methods from, its instance should not be null.
Yes, the Application.onCreate() is called always when the app comes to foreground, so every Singleton that you need to initialize must be there.

Why unbind Service onDestroy?

I've seen it mentioned in multiple sources, that if an Activity binds a Service, it should unbind it onDestroy. Why? Since the Activity is destroyed, it seems like the service will be unbound anyway. If it was "started" - it doesn't matter anyway. And if it was auto-started by the activity - it will close anyway if no others bound it.
So why unbind it?
Activities need to handle configuration changes, such as when the screen is rotated, or the user changes locales, or the device enters night mode.
The default behavior of the foreground activity, when a configuration change occurs, is for it to be destroyed and recreated.
As a result, calling bindService() on an Activity is not a good idea. We want the binding to remain intact across the configuration change. Otherwise, our service will get destroyed and recreated, along with the activity (assuming that the activity has the one-and-only binding and nothing else started the service).
So, the recommended pattern is to call bindService() on the Application singleton. Then, you can pass your ServiceConnection from the old activity instance to the new activity instance. Retained fragments work great for this, as you can then call unbindService() in onDestroy() of the fragment, so that when the activity is "permanently" destroyed (e.g., user presses BACK, you call finish()), your binding can be released.
With all that as background, on to your specific concern.
First, you assume that a destroyed activity automatically unbinds from any services that it bound to via bindService() called on that Activity. It's possible that this happens, though I do not recall that being documented behavior, and it's the sort of thing that developers should not rely upon.
More importantly, in most cases, calling bindService() on the Activity is not the right approach. Otherwise, you get into the problems that I outlined above.
But, following the call-bindService()-on-the-Application pattern, I would not expect there ever to be some sort of automatic unbinding, because the Application singleton is never destroyed. So, if you fail to call unbindService() somewhere (e.g., in onDestroy() of the retained fragment), you will leak your service.

Long running tasks with callbacks to activity or fragment

Question: What is the best practice for reporting progress/complete from long running task to an Activity? And what to do when the progress/complete report happens while the Activity is in the background/orientation changes?
Real life example:
An Activity makes a network call getting data from a server (this could take 10+ sec).
When this network call is finished, the Activity should be notified and the Activity should show that the network call is finished.
This is easy to implement as long as the app stays open. My problem is, what to do if the network call is finished while the app is in the background (activity will miss any callbacks).
I have been looking at the following ways to do this, but I can not decide what to do:
Service that spawns a thread where the network call is executed. The Service is bound to the Activity. On network call finish, Service callbacks to the Activity. If Activity is in background when the Service makes a callback (therefore the Activity misses the callback), should the Activity poll the Service for saved data?
IntentService that broadcasts the data when network call is finished (what to do if Activity misses this broadcast because it is in background?)
AsyncTask, but this is bad when Activity is in background etc.
How should I approach this problem?
I've solved this problem using HeadlessFragments as its called. This blog post explains in detail how to implement it and the concerns you mentioned are handled by it.
EDIT:
For the questions in the comment:
To your specific question, about callbacks being lost when Activity is in the background, the answer is no. Being in "background" means that Activity is still alive. From the link I posted, the callback, which is the Activity itself, is removed when its detached from the Activity i.e when the Activity is destroyed. So, if your Activity is in the background and not destroyed, it'll still get the callback and do whatever you do in that callback. Although Android can kill Activities that are paused, in which case, your Activity will be destroyed and you won't get the callback. In such case, you can either save the data you get from server in a persistent storage, like SQLite, and prevent making another network call or make the network call when the Activity is created which will ensure that whenever the Activity is created, you'll have data to display (given of course, the call goes through).
The use of Fragment was specifically to handle the configuration change you mentioned in your question. The running task is still being done by the AsyncTask and not by the Fragment. The Fragment only holds a reference to the object. So, I'd argue about it not being a "best practise".

When is the object of Activity sub class created in Android?

We write different activity classes in Android and declare those activities in AndroidManifest.xml
However, what I am not getting is when is the object of that Activity class created or how that activity class is loaded into memory ?
The doubt might seem naive but am confused.
An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View). The activity contains the user interface of your application. There are various states of activity like Running, Paused, Stopped and Killed. The Activity base class contains several events that govern the life cycle of an activity.
Now, when Activities's onDestroy method is called, your Activities aren't necessarily garbage collected. When the system gets low on memory the process that your Application lives in can be killed, meaning your Application will disappear; Application's onTerminate method may or may not be called. At that time all the Activities, Services, etc, are killed too. The Application instance is always instantiated first, an Activity must have an associated Application, just like how you define it in the AndroidManifest.xml.
As always, the best resource for understanding all this is the official documentation.
An Activity is instantiated automatically by your Application when it receives an Intent that corresponds with one of the Activities you describe in your manifest. You don't need to worry about any constructor methods, or keeping a reference to your activity after its instantiated. That is done behind the scenes. Android handles the lifecycle, so you use the lifecycle callbacks to handle creation and cleanup of your own objects.
When someone clicks the icon for your app in the launcher, what actually happens is the launcher sends an Intent to your application to launch the activity associated with that Intent. If your application isn't open yet, Android will launch it so it can receive the Intent.

Is there any way to force the IntentService not to be recreated?

i have problem that beetwen two subsequent invokes of startService the private data of IntentService is cleared - i assume that the service is stopped after first invoke is finished, and then recreated when handling second invoke. Is there any way to force IntentService not to be recreated or to store this private data to be the same in two subsequent calls of startService routine?
If you want to try to keep data across instances, you can put it in a static variable. A typical example: you have some state you load of disk, so you make a global singleton that loads the data when first instantiated, and then further requests for it use the same instance (without re-loading the data). When a new instance of your service retrieves the singleton in its onCreate(), it doesn't need to be re-loaded if a previous service instance had already requested it.
When doing such a design, it is very important you understand the lifecycle of your process and when it can be killed. Otherwise you can lose data at fairly random points when your process is killed during normal operation.
i assume that the service is stopped after first invoke is finished, and then recreated when handling second invoke.
No. It will only stop when all Intents received have been processed.

Categories

Resources