I have been wondering for a while now in this question on stack overflow one solution for passing complex data in-between activities on Android is the usage of static data members.
But somehow I get the feeling that this is not the way how you should do it even thou it seems to be the easiest way.
Therefore my question what is the downside of using static members for passing complex data.
A static reference to pass data between components could be used if all of these conditions are met:
It is only accessed via 1 process
The data it contains does not hold a reference to any specific component (Activity, Fragment, Service e.t.c) except for the Application (which is a static singleton anyway)
The data does not keep a reference to anything else that may not have the same life cycle as the data
The data is too complex, or large to reasonably Serialise, Parcel or otherwise pass via Bundles, and doing so would effect the performance of the application.
It is correctly synchronised if accessed from different threads
The data is correctly managed and released when no longer required, avoiding leaking the memory needed to store it
Since these are quite restrictive conditions, a different approach (some suggested by gmale) is usually better.
The biggest drawback in my opinion is the management of the static data - it is usually difficult to tell when the data is no longer needed and when it can be released.
Using static members should be avoided. It's one of the worst ways to pass data in Android. Static objects persist beyond the application's lifecycle. So a user can back out of your app and your data lives on because the class is still loaded in the JVM. That's very bad.
Sometimes, Otto can be a good option for passing complex data around and it plays nicely with the Activity Lifecycle. Another alternative I've seen on many projects is good old fashioned listeners or callbacks. There's also intent services and local broadcast managers.
There are a lot of ways to share complex data. I would consider "static" objects a non-starter. Avoid them like the plague.
Related
I have to solve a theoretical problem and just want to get feedback about the best pattern / practice to solve this in android.
Task:
An app could have several activities / fragments showing a list of objects. If one object is present in more than one list at the same time, I want to synchronize changes to this object. So e.g. if I delete the object it should disappear in all lists.
The restricts are that
I don't know which lists are currently instantiated
Every list has it's own object instance. Data is not shared directly between lists
My solution so far would be to
define a Storage class which provides methods for data access
create a custom Application class and instantiate the Storage class with the application context (which is needed for e.g. content provider access)
the Storage class defines a Listener where each activity can register itself
if an activity modifies an object, which it has to do through the (Application) Storage, the Storage class sends an event to each activity / fragment registered so they can adopt the changes.
Does anyone know a better solution for that? And is my solution conform with the android lifecycle?
Thanks a lot for every kind of useful feedback
If you want to deal in Object then read up on implementing Services and Binding to them. You can provide all your objects with your own API methods and return values. If your data fits into a data cursor model, then you should read up on building your own ContentProvider. It has data observation built in and all the Adapters support it already.
Your solution seems pretty good. You could use an EventBus like Otto http://square.github.io/otto/ to send an event to all your activities/fragment when an object is modified.
If you insist on maintaining the separate lists in separate Activity/Fragment instances, then you will be prone to lifecycle problems (you might miss an object modification event while you're paused or something). Otto or other EventBus libraries might help.
However, I think it would be much simpler to host all those lists (and their possibly-shared objects) in a local bindable Service. Each Activity/Fragment can then simply bind/unbind to it in onCreate/onDestroy, thus guaranteeing an up-to-date view of those lists.
See http://developer.android.com/guide/components/bound-services.html
Another similar solution would be to host all those lists in a retained Fragment. Using retained Fragments would be simpler than a Service because you don't have to write all the bind/unbind code. However, retained Fragments might not work as well as a Service if you have multiple Activity components that need access to the same lists of objects.
Yet another hacky solution is to just host all those things in the Application instance itself (effectively a global variable). Then your Activity/Fragment code can just call ((MyApplication) getApplication()).getFooList() to get to those objects.
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 know this question has been asked multiple number of times and i have been through a lot of these questions......almost all of these questions throw up the use of the parcelable interface for your class.
However in a couple of questions i came across a quotation:
"NOTE: Seeing Parcelable might have triggered the question, why is Android not using the
built-in Java serialization mechanism? It turns out that the Android team came to the conclusion
that the serialization in Java is far too slow to satisfy Android’s interprocess-communication
requirements. So the team built the Parcelable solution. The Parcelable approach requires
that you explicitly serialize the members of your class, but in the end, you get a much faster
serialization of your objects.
Also realize that Android provides two mechanisms that allow you to pass data to another
process. The first is to pass a bundle to an activity using an intent, and the second is to pass a
Parcelable to a service. These two mechanisms are not interchangeable and should not be
confused. That is, the Parcelable is not meant to be passed to an activity. If you want to start
an activity and pass it some data, use a bundle. Parcelable is meant to be used only as part of
an AIDL definition."
This quote can also be found in the book Pro Android 2.
Now seeing that all activities within the same application run in the same process(Every Activity in Android is a Process,or One Application is one process),unless otherwise specified in the manifest,communication within the activities of the same application is not Interprocess communication per se.So is it really faster to use the parcelable class or is it just enough to pass your object attributes through bundle via intent ?
Shedding any light on this aspect will be largely appreciated.
Cheers !!
There's a FAQ for that. :)
The short answer is that the Android team recommends three techniques for passing data between activities and services within an app: a singleton class; a public static field or method; a HashMap of WeakReferences to Objects (and you pass the key in the intent). The major issue to keep in mind is how your data is going to behave under various lifecycle events. (For instance, if the user turns the phone, by default your activities will be destroyed and recreated; your data handling method needs to be designed with that in mind.)
The Parcelable construct is designed to be very fast at passing data across application memory boundaries: within an application you are MUCH better served using Bundle because all the memory locations the data is stored in are accessible to both the sender and the receiver. Since the in-memory objects are accessible there is no need to incur the cost of reconstruction: just use the Bundle, which is really just a glorified HashMap with type-specific put/get methods.
For AIDL and IPC purposes you can't (by default) share memory locations so you need an efficient way of moving data: this is where Parcelable kicks in. Unless one of the components of your application is using the remote process capability then there is no need to use Parcelable.
From docs:
Parcelables
The Parcelable protocol provides an extremely efficient (but
low-level) protocol for objects to write and read themselves from
Parcels. You can use the direct methods writeParcelable(Parcelable,
int) and readParcelable(ClassLoader) or writeParcelableArray(T[], int)
and readParcelableArray(ClassLoader) to write or read. These methods
write both the class type and its data to the Parcel, allowing that
class to be reconstructed from the appropriate class loader when later
reading.
There are also some methods that provide a more efficient way to work
with Parcelables: writeTypedArray(T[], int), writeTypedList(List),
readTypedArray(T[], Parcelable.Creator) and readTypedList(List,
Parcelable.Creator). These methods do not write the class information
of the original object: instead, the caller of the read function must
know what type to expect and pass in the appropriate
Parcelable.Creator instead to properly construct the new object and
read its data. (To more efficient write and read a single Parceable
object, you can directly call Parcelable.writeToParcel and
Parcelable.Creator.createFromParcel yourself.)
Bundles
A special type-safe container, called Bundle, is available for
key/value maps of heterogeneous values. This has many optimizations
for improved performance when reading and writing data, and its
type-safe API avoids difficult to debug type errors when finally
marshalling the data contents into a Parcel. The methods to use are
writeBundle(Bundle), readBundle(), and readBundle(ClassLoader).
This is a question I've now had for a few different apps I've built, and I have yet to be satisified with any of the solutions I've come up with. I thought I'd put it out there to the community to see other solutions there might be.
Let's say you have an Activity that downloads a complex tree of data (in this case via json, but it could be anything), unmarshalls that data to a set of java objects (in this case using gson, but again, could be whatever), then spawns additional activities to view different parts of that data. There might be one activity to view Trips in your response, and another to view Flights in those trips, and maybe another to view Passengers of those flights.
My initial implementation of this app was to unmarshall all the Trips in the first activity, then pass them by value (as an extra in the intent) to the TripActivity. The TripActivity then passes individual flights to the FlightActivity, and so on.
The problem with this is that there's a noticeable pause between activities while the app serializes and deserializes the data. We're talking several seconds. The pause is quite noticeable when my tree uses Serialization or Parcelable to pass data around. Initial performance testing with using google's Parcelable instead show a roughly 30% speedup over serialization, but Parcelable is difficult to work with and doesn't seem to handle circular object references well like Serialization does, and besides it still pauses for almost as many seconds, so I've put that experiment on the backburner while I try other things.
So then I tried moving the tree of objects directly into the Application class. Each activity just gets the tree directly from the app whenever it needs it. This makes performance quite snappy, but handling corner cases like unexpected activity start/stops (either due to activity crashes or because the activity has been closed temporarily to make more memory available, or whatever other cause) seems tricky. Perhaps it's no more than implementing onSaveInstanceState(), I'm not sure, but the solution seems a bit hacky so I haven't investigated further yet.
So in search of a less cobbled-together solution, I tried creating a custom ContentProvider to store and retrieve my objects. Since ContentProviders can be configured to run in-process using multiprocess=true, I thought that would be an excellent way to avoid serialization costs while doing something more "standard" than storing data in the Application object. However, ContentProviders were clearly not intended to return arbitrary object types -- they only support types such as numbers, strings, booleans, etc. It appears I can finagle one to store arbitrary objects by using ContentResolver.getContentProviderClient().getLocalContentProvider() and accessing my custom class directly, but I'm not sure that's less hacky than storing data in the Application object.
Surely someone must have a good solution to this problem. What am I doing wrong?
In addition to fiXedd's solution, another one is to use a local service. Have the service "own" the objects, with activities calling service APIs to get whatever it needs. The service can also be responsible for fetching and parsing the data, encapsulating that bit of logic.
The Application object is the "red-headed step-child" of Android components. Members of the core Android team have come out against the practice of creating custom Application subclasses, though it is certainly supported by the API. Having engineered one ADC2 200 application that leveraged a custom Application subclass, I can say that I should have gone with a service in my case as well. Live and learn...
By using the local binding pattern, your service will automatically be created and destroyed as needed, so you don't have to worry about that. And, by definition, a local service runs in the same process/VM as your activities, so you don't have to worry about marshaling overhead like you would in the ContentProvider scenario.
The way I'm handling this in one of my apps is downloading the data then shoving it into a database. This way I don't have to carry all those objects around (which, IIRC, eat about 1kb each just for the object instantiation) and I can easily pull just the data that I need. I don't know if this will work for you, but it worked for my use-case.
Another approach would be to save the data objects to a shared preferences file. That's how we implemented one of our apps, but I didn't like that approach because it seems too slow.
It's bad coding practice, but the fastest way may be to just use a service to parse the data and save the data to a static class that you can use for the rest of the app's life.
Is there any reason not to use the Application class to share variables across Activities?
For instance a handle to the DB or a single HttpClient.
According to the official documentation "you can use it to maintain global application state". IMO you wouldn't want to hold onto to too many heavy objects, globally. Ofcourse, "too many" is too fuzzy:) There are other ways of sharing (persistent) data: Preferences, database, files. You might want to check whether any of these are a better fit for your problem.
The other thing that you need to understand about when using Application object is its Life cycle. Unfortunately, discussion of that can be very subjective but fortunately, such as discussion has already been done
Good luck!
The Application object is meant for maintaining Application state rather than resources. Personally, I would initiate DB connections or HTTP clients in each Activity that needs them rather than globally in an Application object.
I don't think there's a huge overhead for creating such objects and it means that when an Activity is stopped those resources can be freed up. If you create them in the Application object they will always be instantiated, using up memory, even if the current Activity doesn't need them.