I am developing an Android app and I would like to avoid reloading similar data when it comes from the same Activity using the same extra.
Specifically, when I launch my Activity 'A' with extra, I use this extra to load remote data from server.
From this Activity, I can relaunch 'A' with different extra and so on.
Example :
A:id1 --> A:id2 --> A:id3
But, it can also be an extra that I already loaded :
A:id1 --> A:id2 --> A:id3 --> A:id1
In this case, I wouldn't request the server again or lose the activities stack.
As I understand, "onSaveInstanceState" allows to save one instance of one Activity, but in my case, it's multiple instances of same Activity (with differents extras).
So, is it the solution to manage a list of saved instance states (bundle) for the same Activity ? Or something else ?
Thanks in advance
The onSaveInstanceState method isn't used in the way you describe. Check this out, it's the documentation for the Activity Class, specifically the Activity Lifecycle section. onSaveInstanceState is a method that gets called when the OS has to kill an Activity for some reason. It allows you to populate a Bundle which will help recreate that specific instance of the Activity where the user left off. Usually this happens because the user switched to a different app and the OS is killing the Activity to reclaim memory, but also happens on screen rotation, so it's a nuance of the platform that is important to at least be aware of.
As for your question, what I would do is use a database to store the information that is retrieved from the server. When you start an Activity, you can first check to see if the data that needs to populate that Activity exists in the database. If it does, load and display it from there, else make the server call.
This is nice, because the data will be persistent over multiple uses of the App. Going further, if the data from the server has the potential to be stale, you can easily extend this to display the data from the database initially, and fire off an asynchronous request for the data that will update both the UI and database when it returns. Your user will almost never be in a state where they're waiting for things to load, which is always a good thing!
Here's a good tutorial on the basics of implementing an sqlite database. This will also give you the added benefit of keeping the data stored over separate runs of your application.
As an alternative, if you don't really need the persistence or other features of the database and don't think that the overhead is worth it, you could create a Singleton class which keeps track of the data as its returned, perhaps implementing it using the Application class. It's important to note (and bringing us full circle) that any in-memory method of storing this data should be managed with onSaveInstanceState to ensure you don't lose any data if the Activity is killed at an unexpected time.
+1 for MattDavis' answer, but I'd also suggest you use what's known as the "singleton pattern".
Essentially, this is a way to cache things in memory. You create a class whose purpose is to hold all the database data you don't want to keep reloading. There's a single global instance of this object, which is initially null. You call a static method that returns that instance. If the static method finds that the instance is null, it creates the instance, populates it from the database, caches it, and returns it to the caller. From that point on, all requests for the instance just return the cached copy. If your app gets killed by the system for lack of resources, and started again later, it transparently re-creates the instance as needed.
This is a very common implementation, and works in a multi-threaded environment:
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
// This is the place where you initialize the instance
// from the database.
}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
I use this pattern in a lot of places; it's very handy.
To really do it right, you should also implement onLowMemory() or onTrimMemory() in your activities to release the singleton instance when resources are tight.
Related
In my app, I inject some singleton manager objects using dagger.
Suppose one of these managers, say myManager, keeps data in a list, say myList.
Also I have a BroadcastReceiver class, say myBroadcastReceiver which calls one of myManager methods, say myMethod, when it receives some particular intents.
If I open my app and wait until myList is initialized, then press home, and after that myBroadcastReceiver receives the intent and calls myMethod, myList is null (though myManager itself is not null).
I can't figure if it's a matter of android's natural behavior or if I've actually made a mistake that it happens.
I think your singleton class is getting unloaded due to low memory you need to save that list in onSaveInstanceState(Bundle outState) and retrieve the same in onCreate method
You could either implement a "save state" solution using onSaveIntanceState functionality provided by android in your activity or service or whatever component you're using
Or you can save your data locally in a file or even better a sqlite database, which could be very simple and straight forward to implement.
And by keeping your list "in memory" synch'd with the local storage, you'll never lose any data.
Simply using lazy loading, each time your list is null, fetch the data back from local storage.
I have created an Android application that uses a singleton to hold its state. The class is instantiated when the application starts.
The application does make extensive use of fragments but is not a single Activity application.
The problem occurs when an activity crashes for some (any) reason.
Normally, Android closes the activity bringing to the foreground the previous one that was active. Since the previous activity also used the singleton somehow, it needs data from it to resume (for example). The thing is that the singleton is no longer available while the previous activity is running in a new Application Context forcing the Singleton to re-instantiate itself with no data of course.
One way to surpass this problem seems to be storing the state (serialized or not) in a file or the database but that means too many read writes on pretty much every other user activity which should be avoided. Apart from UX, this solution might lead to inconsistent or erroneous data from faulty or untimely synchronisations.
I would like to hear your input on the matter.
Cheers!
Here is the Singleton instantiation method.
final public class Data {
private static Data INSTANCE = new Data();
private Data() {}
public static Data getInstance() {
return INSTANCE;
}
}
If you declare your singleton as static on a root activity it should be saved. We used static varaibles linked to a baseline activty from which every activity dereives.
Antoher solution might be to use sharedpreferences.
It is nice to worry about performance but if you have many fragments and few activities, it would be the fragment that will cause the lion share of teh workload
1) You can use an Application class. It's gonna be destroyed at the very last stage.
2) If your data is not structured - you can keep it in SharedPreferences (as a String mapped with JSON for example)
3) If your data is structured - you should store it in DB (or better ContentProvider)
A few days ago I've discovered that singleton can become anti-pattern in Android. My singleton (class with private constructor and instance stored in static field) was deleted (instance was deleted despite the fact other activities were still using this singleton (via getInstance() method) so another instance had to be created ) because Activity from which it was first invoked was deleted (after invoking finish for only this one activity).
I've read already how this problem can be resolved however I've also just read "Effective Java". There is said that "Single-element enum type is the bast way to implement a singleton".
So now I'm wondering what would be the lifecycle of singleton created this way in Android application? Would it be the same like in case of "standard singleton implementation" so after destroying activity from which it was invoked the first time it will be destroyed (even if it used also in other activities)?
I'm not asking about proper android singleton implemenation or the singleton pattern itself (is it pattern or anti-pattern etc) but I'd like to know what be the lifecycle of such enum singleton object and when it will be destroyed.
In all cases, the classes you use are tied to the ClassLoader that loaded them. This is true in Java in general, not just Android. Android will isolate activities by using new ClassLoaders each time -- at the least, it doesn't promise it won't, and it does as far as I can tell.
Any singleton, or other class-level state, is tied to the Class which is tied to the ClassLoader. This is why your state "disappears"; it's actually that your calling code is seeing a new Class in a new ClassLoader.
So, your enum-based trick, or anything else along these lines, would have exactly the same behavior. You just can't "persist" activity information this way. You can and should write to a SQLite DB. You could probably stash it in the SharedPreferences too.
The application object is a good location to store information which needs to be accessible to various activity or service instances. You can retrieve it like this (where this is an Activity or Service):
private MyApplication app;
in onCreate(...){
...
this.app = (MyApplication) this.getApplication();
...
}
Remember to set the name also in the manifest:
Set the attribute "name" of the "application" tag:
The value is the path to the class relative to the package of your app.
The application object is created when the app is started, you can initialize like in an activity or service in it's onCreate() method.
One thing to remember: The application object can survive closing your "last" activity. In this case you may get the same application object with the state from the previous interaction with your app. If this is a problem you must somehow detect the initial start of your app (e.g. using a special launcher activity without UI, which initializes the application object and then starts a new intent.
BTW, the same may happen with singletons if they have not yet been lost to garbage collection.
My secure singleton implementation is like that:
I create a singleton class which has an attribute of boolean 'didReceiveMemoryWarning';
public class SingleTon(){
public boolean didReceiveMemoryWarning = true;
...
...
}
In application first screen(It is exactly first launch screen)(I have a splash screen that is visible 3 sec)
SingleTon.getInstance().didReceiveMemoryWarning = false;
And in every Activity's onCreate() method, check this boolean data,
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(SingleTon.getInstance().didReceiveMemoryWarning){
{ Load your data from local to your SingleTon class,
because your app was released by OS};
}
}
it is my implementation.
This is a pretty simple question, but I have been unable to find anyway to accomplish what I am trying to do...
I want to launch a new Activity to display some complex information. Because of the complexity, it is undesirable to serialize the information into the intent's parameters. Is it possible for the the new Activity to get a reference to the launching activity, so it can call its methods?
If you use a custom application class, you can store information that will be kept between the activities.
See a tutorial here for instance.
The lifetime of an Activity cannot be depended upon. In this case, one way of sharing data is to have a singleton which holds the data to be shared between the two activities.
You can add a public static field to the first activity containing this (the first activity).
But beware that the first activity could be destroyed by Android while you are using the second activity, so you will have to implement a fallback method if the first activity is destroyed.
And don’t forget to unset the public static variable in the onDestroy() callback of the first activity or you will leak memory.
Is it possible for the the new Activity to get a reference to the launching activity, so it can call its methods?
Please do not do that. Android can and will destroy activities to free up memory.
Complex information like you describe should not be owned by an activity. It should be held in a central data model, like you would in any other application. Whether that central data model is mediated by a Service or a singleton or a custom Application object depends a bit on the type of data, caching models, risks of memory leaks, and so on.
You can make your complex objects public and static in ActivityA, and access them in ActivityB like this:
MyCustomObjectType complexFromA = ActivityA.complexObject;
this will work, however while in ActivityB, you can't always be sure that static objects from ActivityA will exist(they may be null) since Android may terminate your application.
so then maybe add some null checking:
if(null == ActivityA.complexObject) {
//go back to ActivityA, or do something else since the object isn't there
}
else {
//business as usual, access the object
MyCustomObjectType complexFromA = ActivityA.complexObject;
}
You could also use a Singleton object which extends Application. You would have the same problem when Android terminates your application. always need to check if the object actually exists. Using the Singleton extending Application approach seems to be the more organized way - but adds more complexity to implementation. just depends what you need to do and whatever works for your implementation.
You should create a separate class that both the activities can use.
public class HelperClass{
public void sharedFunction(){
//implement function here
}
}
I would recommend staying away from static variable in android. It can cause some unexpected behavior.
Use getParent() from new activity and call parent's method
Android Activity call another Activity method
I'm working on a fairly complex Android application that requires a somewhat large amount of data about the application (I'd say a total of about 500KB -- is this large for a mobile device?). From what I can tell, any orientation change in the application (in the activity, to be more precise) causes a complete destruction and recreation of the activity. Based on my findings, the Application class does not have the same life-cycle (i.e. it is, for all intents and purposes, always instantiated). Does it make sense to store the state information inside of the application class and then reference it from the Activity, or is that generally not the "acceptable" method due to memory constraints on mobile devices? I really appreciate any advice on this topic. Thanks!
I don't think 500kb will be that big of a deal.
What you described is exactly how I tackled my problem of losing data in an activity. I created a global singleton in the Application class and was able to access it from the activities I used.
You can pass data around in a Global Singleton if it is going to be used a lot.
public class YourApplication extends Application
{
public SomeDataClass data = new SomeDataClass();
}
Then call it in any activity by:
YourApplication appState = ((YourApplication)this.getApplication());
appState.data.UseAGetterOrSetterHere(); // Do whatever you need to with the data here.
I discuss it here in my blog post, under the section "Global Singleton."
Those who count on Application instance are wrong. At first, it may seem as though the Application exists for as long as the whole app process exists but this is an incorrect assumption.
The OS may kill processes as necessary. All processes are divided into 5 levels of "killability" specified in the doc.
So, for instance, if your app goes in the background due to the user answering to an incoming call, then depending on the state of the RAM, the OS may (or may not) kill your process (destroying the Application instance in the process).
I think a better approach would be to persist your data to internal storage file and then read it when your activity resumes.
UPDATE:
I got many negative feedbacks, so it is time to add a clarification. :) Well, initially I realy used a wrong assumption that the state is really important for the app. However if your app is OK that sometimes the state is lost (it could be some images that will be just reread/redownloaded), then it is fully OK to keep it as a member of Application.
If you want to access the "Global Singleton" outside of an activity and you don't want to pass the Context through all the involved objects to obtain the singleton, you can just define a static attribute in your application class, which holds the reference to itself. Just initialize the attribute in the onCreate() method.
For example:
public class ApplicationController extends Application {
private static ApplicationController _appCtrl;
public static ApplicationController getAppCtrl()
{
return _appCtrl;
}
}
Because subclasses of Application also can obtain the Resources, you could access them simply when you define a static method, which returns them, like:
public static Resources getAppResources()
{
return _appCtrl.getResources();
}
But be very careful when passing around Context references to avoid memory leaks.
Dave, what kind of data is it? If it's general data that pertains to the application as a whole (example: user data), then extend the Application class and store it there. If the data pertains to the Activity, you should use the onSaveInstanceState and onRestoreInstanceState handlers to persist the data on screen rotation.
You can actually override the orientation functionality to make sure that your activity isn't destroyed and recreated. Look here.
You can create Application class and save your all data on that calss for use that anywhere in your application.
I know this is the very old question but using the ViewModel from the jetpack components is the best way to preserve the data between Activity rotation.
The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.