Pass custom objects between activities using Intent - java

I wondered if it makes sense to make a class that inherits from the Intent class and that overloads the putExtra method, allowing to pass custom objects, instead of using Parcelable.
Is it something sensible? Is it much slower than passing parcels?
I would like the process to be extremely fast, keeping the object in memory, and from what I understand, parcels use serialization instead.

It's an interesting question. Intent is a universal data wrapper that, among it's other functions, allows you to transfer data between processes, that's why you need your data in parcelable form.
In fact the intent itself is parcelable, and to implement a proper subclass you'll need to parcel all your added fields and you'll basically end with the same intent class, just with a few exposed fields.
Here is a subclass of Intent for reference: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/content/pm/LabeledIntent.java
So I would not recommend you to follow this approach. If you want an alternative way of sending data between different components I would recommend you to use concept of EventBus and create something like https://lorentzos.com/rxjava-as-event-bus-the-right-way-10a36bdd49ba

Related

Pass Object reference within Intent without implementing Serializable or Parcelable

I know similar questions have been asked multiple times. I think i read most of it. But none answer is applicable.
I need to pass complex Objects via Intents (Activity calls/Broadcasts). Everything is done within my process. That's why I see no reason to write my objects into Streams just to reassemble them a few milliseconds after. I want to pass my object reference through my application. Is there any way to do this.
Since my application will broadcast the same Event multiple times in a row I can't rely on static members. I need to get exacly the same object for what I broadcasted.
That's why I was thinking about a static "Referenceholder" that will accept an Object and return an integer that identifies this object in it's internal list so I can pass this integer via .putExtras. But as far as I know Java I could not clean up this Object from this list after it has been added because multiple Listeners could be interessted in the very same object and I would have to keep it in my Referenceholder for ever (assuming that a thread may be resumed at any time - even 2 minutes later).
Any ideas? Am I doing something wrong? Or any ideas of how I can clean up my referneces (probably after some seconds? this may lead to a crash but it seems to be more applicable than writing code that assembles and reassembles my objects for no reason)
Your options are pretty clear: There is no way to pass an un-marshallable object (Parcelable, Serializable) in an Intent. Full stop.
What you might be able to do is to pass something that is a reference to an un-marshallable object. The idea is that you would do something on the order of passing a key to a map that maps that key to the value that you are interested in passing. If both the Intent sender and the intent recipient have access to the map, you can communicate a reference to the un-marshallable object.
I don't understand, exactly, why you think static members are not what you want. I would guess that a static map, in a custom Application object, would be pretty much exactly what you want. ... and I suspect, from your comment about WeakHashMaps, that you've discovered exactly that.
... except that nothing you've said so far explains why you want to make your map Weak. Before you use a Weak map, have a look at Soft references, to make sure that that is not what you mean.
Best of luck
EDIT:
Forget about this solution. It does not work. Android is coping the Intent that you pass in .startActivity(). There is no way to get any reference inside a activity. This is - in my opinion - great bu****t my by google. You have to call your activity and place the referneces of your object in static members...
As metioned by G. Blake Meike, there is no way to pass Object references in Android via Intents. But you maybe can use WeakReferences.
A very excelent aticle about this topic is found here:
http://weblogs.java.net/blog/2006/05/04/understanding-weak-references
I got to that solution through this question:
Is it possible to get the object reference count?
So what I'm basically going to do is:
I will use Intents as a Key for a WeakHashMap and pass my Object as value. This seems to be the only Object that is suitable as Key since everything you put into the Intents extras will be serialized. Due to that, you can only pass one Object per Intent. You could implement Subclasses inside your Acitivity that can hold your Objects an put this Subclass into the map instead. But I'm still not sure if the Intent object that a receiver will get is the same that the caller created but I think so. If it is not, I will edit this solution (or maybe someone could clear that up).

Is Passing Intent Extras Between Activities Bad Style?

It seems to me that passing extras via Intent calls is a violation of encapsulation, since classes are directly communicating with one another. That being said, the only viable alternative to this that I have found is setting a variable in some shared class and just having each activity pull data from it - part of me, however, can't help feeling that this isn't a great design choice either. Can someone shed some light on this?
Encapsulation, as one of the whale of Object-Oriented paradigm, assumes that you free programmers, who will use your class in future, from the necessity to know how does your component built inside and what complex aspects does it hold, carrying out this information to comfortable and clear programming interface (in the best cases).
Intent model was developed so, that it assumes interaction between main components of operating system, especially Activitys, and also it assumes that you need to accompany your intents with some concrete information such as kind of ACTIONs you want to perform, a CATEGORY of your intent and some set of DATA that need to implement your intents. So this is convenient model in such kind of interactions.
At the same time, using some shared class between activities when there is a ready solution for that, is rather irrelevant approach, IMO.
It isn't, you'll end up needing it. Usually I pass minimum info between activities and then check out big data in the launched activity's onCreate().

Why we need to serializable object for passing one activity to another activity in Android

Can anybody please tell why we need to serializable object for passing one activity to another activity in android? Android is following Java syntax. In java we can pass object to another class without serializable.
Thanks
In ordinary java programs passing parameters(Object type), is kind of create a new handler to the object and giving to another method (In regular words passing the reference by value).
But when it comes in android, passing object references from activity to activity, where their states have to be persisted, is a serious headache.
One way you can do is create a static object in the first activity and access from the second, though this seems to be a easiest way, there is no guarantee that the system maintains the activity in the memory. Therefore the second activity may loose the object reference.
Other way, and the mostly recommended way is serializing(Kind of flatten the object) the object and pass with the intent as extra. In android there are two ways to serialize.
Implement the java's serializable interface
Implement the android's parcelable interface
However, on the android, there is a serious performance hit that comes with using serializable, the solution is using parcelable.
You can find a pretty good tutorial and explanation on android parcelable implementation here.
We need to understand following concepts before getting to the answer:
Android uses Binder for inter-process process. It is required even for simple app because the OS and the apps run in different processes.
Marshalling:
A procedure for converting higher level application data structures into parcels for purpose of embedding into Binder transaction
Unmarshalling
A procedure for reconstructing higher-level application data-structures from parcels received though binder transactions.
You can consider Intents as higher level abstraction of Binder
Based on the documentation following is the way how intent communication occurs:
Activity A creates an Intent with an action description and passes
it to startActivity().
The Android System searches all apps for an intent filter that
matches the intent. When a match is found,
the system starts the matching activity (Activity B) by invoking
its onCreate() method and passing it the Intent.
Why Parcelable or Serializable
IPC (Inter Process Communication) requires data in Intent to be Marshalled and unMarshalled. Binder provides built-in support for marshalling many common data-types. However when we define custom object, it would impact this process and the final object received might be corrupted during the process.
When you define custom object, you need to be responsible for providing this marshalling and unmarshalling which is achieved through Parcelable and Serializable (Since comparison between these two would be another topic I won't discuss much here). Both of these provide mechanisms to perform marshalling and unmarshalling. This is the reason why you need to use Parcelable or Serializable.
Using Parcelable you write the custom code for marshalling and unmarshalling the object thereby you gain complete control over the process.
Serializable is a marker interface, which implies the user cannot marshall the data according to their requirements and its done on JVM, which doesn't give any control at your side.
Disclaimer: Description above is my understanding for the rationale behind the need for serialization based on some
documentation
There are basically two questions in your question, so let's decouple it.
Why marshall in a Parcelable instead of passing an object reference directly?
It's obvious faster and more memory efficient to reference objects rather than marshall/unmarshall them. So you shouldn't use Parcelable when you can pass the object directly.
However, there are situations where you may not have access to the object reference.
in Intent because the process that handles the Intent may not be the process that emitted the Intent (it's an inter-process communication)
in Activity lifecycle, for instance in onRestoreState(), because the whole app may have been killed by memkiller when the user wants to resume it.
everywhere else where Android frameworks requires
In IPC, why use Parcelable rather than Serializable like Java does?
That's only a performance optimization.
If We want to pass object from Activity to to Another Activity . We need to save the passing state.
//to pass :
intent.putExtra("MyClass", obj);
// to retrieve object in second Activity
getIntent().getSerializableExtra("MyClass");

Is it possible, and what is the best strategy, to pass objects by reference from one Activity to the next?

I have a simple Android application that uses an instance of a class, let's call it DataManager, to manage access to the domain classes in a Façade-like way. I initially designed it as a singleton which could be retrieved using static methods but I eventually got irritated with the messiness of my implementation and refactored to what I reckoned was a simpler and cleaner idea.
Now the idea is that for each file that is opened, one DataManager is created, which they handles both file I/O and modification of the domain classes (e.g. Book). When starting a new Activity, I pass this one instance as a Serializable extra (I haven't got on to using Parcelable yet, but expect I will when I have the basic concept working), and then I grab the DataManager from the Intent in the onCreate() method of the new Activity.
Yet comparison of the objects indicates that the object sent from one activity is not identical (different references) to the object retrieved from the Bundle in the second Activity. Reading up on Bundle (on StackOverflow, etc.) suggests that Bundles cannot do anything other than pass-by-value.
So what is likely to be the cleanest and safest strategy for passing an object between Activities? As I see it I could
Forget about passing by reference and live with each Activity having its own DataManager object. Pass back the new DataManager every time I close an activity so that the underlying activity can use it. (The simple solution, I think.)
Go back to using a singleton DataManager and use a static method to get it from each Activity. (Not keen on using singletons again.)
Extend Application to create a sort of global reference to DataManager. (Again, not keen on the idea of globals.)
Is that a fair summary? Is there some other healthy strategy I could use?
Another approach would be to create a service. The first activity would start the service and bind to it, when you launch a new intent, unbind the first activity and when second activity starts, bind to the service.
This way you don't have to ever stop the service or worry about passing data between activities.
Java does not have pass by reference so that option is out, I would suggest dependency injection for passing data between the activities. Otherwise definetely the singleton would be the way to go.
The prescribed one is Going by implementing Parcellable interface, thats the way to pass Objects between Activities.. and the 2nd and better choice is to make a Singleton to be sure its single Object.
Create your DataManager as a Singleton that implements Service. Bind the service to your application in the manifest xml (see the link), and you will have a persistent singleton your activities can access without issues.
Passing parcellable arguments can quickly get very messy if you need to get a lot of data. The singleton approach, although usually considered an anti-pattern, works like a charm in cases like these. Just remember to not create multiple singletons that interact with one another.
I would suggest using an Application Subclass. It allows you to hold a single reference to the DataManger class and is persistent as long as your app lives.
A singleton with a static field will also work, but there are some place in the documentation where it says that the content of static fields is not a safe place to store your data. As I understand static fields should persist as long as your ClassLoader stays in memory. Therefore a singleton should only vanish if the whole process leaves the memory and in that case the application class will also leave the memory, but it will call the onDestroy methods of the Application and that enables you to safely close your DataManager and persist important data to memory.
That said to your two variations.
The Android way to go would be to make your DataManager a ContentProvider. This will make it possible to access your Data from every Activity without holding a global reference. I'm not sure how you would build a caching Content Provider that stays in memory and is not reinstantiated too often.

Shallow copy of an object in an intent in android

I have a few objects I want to pass to other activities through intents. However, they only need to be shallow copies of the other object, as they are only going to be read (and even if they were going to be modified, I would want them modified in the original activity too), so there's no point in making the object a serializable, also because not all of the fields in the object are serializable, I can't even do it.
Also, it seems like making it parcelable would run into the same problems. Sure, it seems like I can add some of the fields in it as active objects, but I can't make the whole thing just a shallow copy.
So is there any way I can make a shallow copy of an object in an intent, or any other way of passing a shallow copy of data to another activity, like you would normally pass a parameter in java? Thank you.
JesusFreke is correct that you cannot pass a reference (pointer) to an object via an Intent. An Intent is meant to be serialized data not a reference to that data. However, I would recommend against the static map technique as it is often a source of memory leakage. Instead I would recommend creating a subclass of android.app.Application and using it to store references to whatever data structures that you need to share across Activities.
When you send an intent, the intent and all the data associated with it has to be marshaled across process boundaries. The only way this can occur is if the object is parcelable.
However, if the code that is sending the new intent is in the same process as the activity that is being started by the intent, you could do something like create a static map to hold the data that you want to send to the new activity, and then pass a key to that data in the intent, with which the new activity can look up the data from the map.
However, my only concern with this type of an approach is that it is possible that you send an intent, and then the process dies before the new activity is started. And then the process is restarted for the new intent, but then the data you had stored in the map is obviously gone. I would guess that this would likely be a very rare occurrence though.

Categories