I am considering using the android Application class as a place to store temporary state and common code shared by other (fragment) activities in the app.
I would like to get more feedback as to whether it is a good place for:
Shared constants like ID's, pref key names, etc.
Global variables (i.e. setters/getters) reflecting current UI state, navigation, selected fragment, and, in general, temporary data that does not need to be persisted.
Hooks for persisting data when certain conditions are triggered.
Updating the UI after preference changes.
Providing an easy way to access the context from anywhere in the app, including code where getApplication() is not available, e.g. via a static getter such as MyApp.getApp().
Common methods that need the visibility of the global state variables and which would become too cumbersome to move away to dedicated classes.
What else would be appropriate/useful/handy to have in the activity class? What would not be a good idea to keep in it and what would be the best alternatives? And finally, what you have found Application to be best used for in your apps?
Shared constants like ID's, pref key names, etc.
I generally create a constants file called C for this, as its better for readability. C.SHARED_PREFS is easier to understand that Application.SHARED_PREFS IMHO.
Global variables (i.e. setters/getters) reflecting current UI state,
navigation, selected fragment, and, in general, temporary data that
does not need to be persisted.
This would be better off in the Activity or component which it concerns (for example, the UI state of an Activity should probably stored in the icicle bundle, or within that instance of the Activity).
Hooks for persisting data when certain conditions are triggered.
This should be fine.
Updating the UI after preference changes.
Again, I feel this would be better off in the respective component.
Providing an easy way to access the context from anywhere in the app,
including code where getApplication() is not available, e.g. via a
static getter such as MyApp.getApp().
This would work, but be careful of memory leaks. You should generally pass the context in as a parameter when calling the method from an Activity or Service or whatever. Less chances of a memory leak.
Common methods that need the visibility of the global state variables
and which would become too cumbersome to move away to dedicated
classes.
I feel it would be better to make the effort of dedicated classes, as when your app grows in features and size, this will become difficult to maintain.
It is probably some place where certain hooks can be attached.
For instance, if you use ACRA crash reporting library, you just need to use the Application class, because this is where ACRA is attached. This is that forced me to start using this class; I never needed the one before.
Related
I am building my first Android app. I need to add a specific set of headers to each web api call and want them to persist even when the app closes. I implemented them in shared preferences but now I need to drag around a context for every call, which seems like a bad way to do things. I would like to set the values on initialization of the app. Have them usable by the WebAPIHelper object without much trouble, and when the values change, save them. I am using Room so I could save them there.
Is it standard practice to use LiveData or extend the application object to have access to the values for use within an object such as my WebAPIHelper, or is there another, better way? Is it best to persist these values in a database or SharedPreferences?
UPDATE
I have seen some examples of extending the application object like here and here. I would want to get the values from sharedpreferences onCreate and save them to sharedpreferences by calling a setXXX, but I haven't gotten that to work.
Also I was concerned about this statement I found in an article:
"Storing data in the Application object is error-prone and can crash your app. Prefer storing your global data on disk if it is really needed later or explicitly pass to your activity in the intent’s extras."
The implication of this would mean that I would need to pass the header values to the WebAPIHelper object all the time. I am passing the context object everywhere now so I guess it's 6 of one vs 1/2 dozen of the other.
The other way to do this might be to extend the AndroidViewModel class in a BaseViewModel, add the properties there from SharedPreferences so they are available through all ViewModels in the app. Has anybody done it this way?
What's the best way to save any changes to global state, that would be lost if the process was killed due to low memory etc?
For activities we have onSaveInstanceState() which allows state to be saved when needed.
I would like something similar for global state. Our Application class holds a references to many objects that can be simply reloaded in onCreate() when the app next starts. But we also change the state of some of those objects, and require these changes to be maintained through the app being killed due to low memory etc.
We could of course persist every time changes are made to the objects in memory. But this would be very wasteful.
My current thought is to use onActivitySaveInstanceState() and keep a dirty flag to know only to call it once. But this adds complexity and probably isn't what this method was intended for.
This cannot be a particularly uncommon requirement, but most of the resources on the net focus on just Activity lifecycle. I'd be pleased to learn of a better approach.
There is no concept of Global State and I actually think that you don't need it at all. An app consists of several independent parts which have their own "state". Thus each part is responsible for its own state. For instance, a fragment which shows particular data knows where to get this data and what to do if there is no such data. An activity which shows user settings knows where to get user settings, how to update it and so on. Both of these parts know what to do when lifecycle methods are calling.
You can always store values in your Application class. On a high level, this is very simple, you create a custom MyCustomApplication, that extends the Android Application class. The Applicaiton class only lives, while the App is in scope (including when it's in the background, which is somewhat unpredictable in Android).
Then you would can access this using the getContext().getApplication()
The implementation details are more complex, and you really should extend the Applicaiton into a Singleton, as described in this SO post: Singletons vs. Application Context in Android?
I am new to android development and I see that there are different components (activities, services, etc.) where each component has a different life cycle.
I am encountering a situation where I have an object that should be accessed by different components. Namely, I have a list of objects that changes dynamically, and that list should be accessed by:
An activity that displays the list in a ListView
A broadcast receiver, that responds to alarm events scheduled in the AlarmManager.
The broadcast receiver may kick in hours after the activity was used. At this time, the activity may or may not be still alive in memory.
Is there anywhere I can put the list so it will be accessible to all app parts? My idea was to serialize the objects I need to share, write them to shared preferences and then recreate them whenever needed. I'm not sure it's a good idea, and even if it is - how would I design it? when would writing to shared preferences (or somewhere else) occur? when would reading occur (it would be preferable to read the object from memory when possible)?
You can put it in SharedPreferences if it a compatible data type, or serialize the list to a file in the internal or external storage (see saving files, you probably want internal storage for your situation).
You can read the list in the onResume() method of the Activity or the onReceive() method of the receiver. Writing will occur whenever a new element needs to be added to the list, you will deserialize the list, add the element and re-serialize it. You may need to place the reading and writing in a synchronized block or method to prevent simultaneous access.
If the list may be added to outside of the Activity when the Activity is resumed you may need to provide a broadcast receiver or other mechanism for the Activity to be alterted and reload the list.
As Gabe pointed out, you could store it in a static variable somewhere, but the way you described the situation serializing sounds like a better option. Otherwise the list is gone if your process is terminated by the Android system.
As kcoppock pointed out, you could use an SQLite database to store the data, but this sounds like overkill from what you described. You will be in for some additional coding without much benefit.
What you are looking for is the Android Shared Preferences. You can access Sharepreferences using getApplicationContext() from an Activity or a Service.
Hope this helps
If you don't want it permanently stored, put it in a static variable somewhere (anywhere they both agree to- a separate class would be a good idea). Then you can access the list unless Android kicks the class out of memory- which it should only do it its terminating the app due to lack of memory, in which case it doesn't matter much anyway.
If you do want it permanently stored, the filesystem is the best way. You'll need to synchronize access to it of course.
Just wondering what is a better practice to pass information between activites, adding it to a bundle or using a singleton class to store and access this data. I have used both in the past for various android side projects, but I am now working on an android project that is of much larger scale, so would prefer to do things right towards the beginning.
My application authenticates users and then will have to do various queries based on it's id. To minimize coupling between activities, I would think just adding the id to the bundle, and then letting each activity query for the information that it needs, would be the best bet; however to increase responsiveness, I was leaning towards using a singleton class to store persistent information, preventing more queries than need be.
Personally, I would create an extension of Application to store the state of your app and share data between the different activities. The Application acts as the context for your whole app and Android guarantees there will always only be one instance across your app. Hence it works similar to defining your own Singleton, but using Application will allow Android to take control of the life cycle of your shared data and basically do the memory management for you.
Here are some more details. If you go down this path, you can simply add any getter/setter (or other) method to your application extension to store/retrieve data and do operations on it. Especially the latter can become quite a pain to manage (and keep consistent) when using Bundles passed back and forth between activities. If would only use a Bundle if the data is needed in just one or two places that are neighbours in the activity flow and does not need any (complex) operations to be run on it.
The better way to go for you is to use SharedPreferences to keep userId you need to keep and reuse. Of course you can use singleton approach or even Application class, but the data will be lost after application is killed.
The only time I pass data between Activities via bunlde is if it's something that I won't need to access for a while(i.e the the resID of a resource I want to use only once in the calling activity, etc). I would also think the difference in responsiveness would be very minimal, so that shouldn't be of concern. I suggest the singleton approach
Passing bundles is a tedious job. You'll have to pass a bundle for every change in activity to make sure that the value is not lost, even if you're not using the value in the called activity.
Singleton pattern have some bad results. For example:From Main activity you call secondary activity. Phone call interrupted your work.After ending phone call Android is trying to bring secondary activity to screen. Here is my nightmare - many users complaint about exceptions - Google reported to me NULL pointers in my singleton. So you have to provide not only singleton, but all data inside singleton have to be as singleton too. This maked come very complicated :(
I have been told recently that extending the Application Class in order to use it as a Singleton was a bad practice but without any explanation.
So what are the potentials problem behind the use of this class ? I have seen it used in many projects.
Also, if using the Application Class is a bad idea, what are the alternatives to store application level variables ?
Using a Singleton approach is not really a bad idea, but it may be troublesome in the cases when being used in a multi-threaded environment where one thread sets a value to a variable and the other thread may over-write that value without any notice.
However, in order to keep application level instances/variables, it is suggested to extend Application class and define it in your AndroidManifest.xml as the default one. Since the application's context is created only once (till the application is running and stays in the memory) when you launch that application, so you may define some variables inside that class to make them availabe anywhere in your application's code using public methods.
Moreover, you may use your application class as Singleton too, since its guaranteed to be created only once when launched.
The problem with using a Singleton class, as well as extending the Application class, is that if the application process is killed - which is very likely to happen when the app is left too long in background - the object will lose all your data.
However, using Application class may be a good option in cases when your app is in foreground or does not stay to much in background (although not 100% risk free).
As an alternative, you could save the data into SharedPreferences, or if your object is more complex, save it in a database.
Another option would be to combine both the use of Application, plus SharedPreferences for example. First try to retrieve the variable from Application instance, if the variable is null, then retrieve it from SharedPreferences.