Shallow copy of an object in an intent in android - java

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.

Related

Pass custom objects between activities using Intent

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

Adding data to a parcelable object passed to another activity

Right now I have three different classes that hold data. They're used to track grades. They are Class, Type, and Assignment. They all implement parcelable.
Now, when I pass a Class object to the Activity that represents a list of contained types, it works fine if the list of types is already contained the the Class object. However, when that activity is launched and I add data to it, it does not add it to the Class object such that when I backtrack to my Mainactivity and then click on the same Class object all of the data I've added in the other activity is gone.
Basically, what I'm asking is if you pass an object to another activity does it reference the same object in memory? If not, how do I make it so the information I add in the other activity becomes contained in the object I clicked on in the MainActivity which represents a list of Classes.
Basically, what I'm asking is if you pass an object to another activity does it reference the same object in memory?
No. When you pass around Parcelable via Intent, runtime serialization/deserialzation takes place. It basically means that your objects are re-created with copied data.
This is also why Parcelable (or its lower level representation Parcel for that matter) is used as a fundamental data structure in Android's Binder driver to enable inter-process communication.
If not, how
Since your Class class implements Parcelable, you can readily return a refresh copy of your data via startAcitivtyForResult and onActivityResult in your main Activity. You can find out more from this developer guide.
Of course, you can always persist the data in memory or in file system when you exit the activity, and reload from persistence when your main Activity resumes.

Parcelable, carrying information through activities

So, I'm trying to make something like an rpg for the android to practice programming. I have a hero object which I'm trying to pass using parcelable as recommended by others, but I'm not sure how to pass it back.
In one activity, I'll have
myIntent.putExtra("heroData", hero);
And then, in myIntent, the activity started in the original activity, I'll have
hero = (Protag) getIntent().getParcelableExtra("heroData");
note: Protag is the class of the hero object
So, the first activity does successfully pass the object to the second activity, but such that the second activity doesn't affect the object in the first activity. Like, if something happens to the object in the first activity, it'll preserve to the second activity, but if something happens to the object in the second activity, the object in the first activity is still the same.
How would I make an object that can be changed by any activity such that the changes are preserved through other activities?
you are now dealing with concurrency. Since you have 2 'activities' possibly acting upon a single 'data'. However, Parcelable is a serialization technique, which means that you will lose reference once you pass a parcelable object(via parcelable). which means that you can't use a parcelable object as a central point of synchronization (both updating it with data).
If you will ONLY EVER plan on having a single object of your class, then you can make the object have static values. Otherwise this is the wrong mechanism to do this.
An AIDL one-way ascync service that notifies each thread of changes to a 'registered' object's values, is viable. (AIDL one-way is actually not hard to write, just takes some practice) Here is a project I wrote that makes use of it, to show both sync and async service usage. (My PhD Adviser's REPO for the MOOC he teaches)
Update to explain why I say this is 'concurrency'.
First of all I take a 'wide breadth' approach to what is 'concurrent' vs what you might be thinking. Your working definition of 'concurrent' is a valid one. However, it is somewhat limited in scope compared to what I'm thinking about. (usually doesn't matter, but the nuance of Android's lifecycle actually makes it important)
Android Activities have 6 life-cycle states they could possibly be in.
Created
Started (Visible)
Resumed (Visible)
Paused (partially visible)
Stopped (hidden)
Destroyed
Now here is where the issue of concurrency comes up.... When you have 2 or more Activities that are not in the 'Destroyed' state.
Plus there are a lot of things that add unpredictability to your logic that you have to think out very carefully...
Android's non-deterministic behavior.. User could press power/home/back or some other button, a phone call comes in/AMBER Alert takes priority over the phone, and/or the Garbage Collector "magic divine/unholy ritualistically" decides who lives or dies (exaggerating, but you get the point).
But lets assume that A doesn't get killed (which is actually the 'problem' scenario here).
So A makes an X object (hero or whatever) and passes by value (parcelable+intent) to B the value of X. At this time, the value(and reference) of X is in A. but the value of X is in B. Therefore, we are already in concurrency. Because the life-cycles of A and B overlap. Not just the "visible" portions of the life-cycles. So this means... where do you put the logic on "when to pass back the value of X"? Do you pass it back on onPause()? (but how? an intent wouldn't work if the User presses the back button)
Short answer: There isn't a 'great' way to do this. (at least with only Activities and Intents/Parcelables.)
You need some concurrency mechanism in place that allows the changes to X in A to propagate to change the values of X in B. (And this needs to be done reliably, correctly, and efficiently.)
The base goals of concurrent programming include correctness, performance and robustness. (from WIKI:Concurrency(CS) )
Ideally you wouldn't pass the data by value, but instead by reference, and when one class updated X (be it A or B) there only ever existed one value of X. That way when A or B was re-started it would have a 'valid' value of X. But you can't do this 'nicely' through parcelable data.
Therefore I would use a service to be the authoritative keeper of the 'value of X', any time A or B wants to update X, then they have to go through synchronized methods to get/set X. (I've never really ran into this scenario where two activities want to have the same 'live data' like this, so perhaps there is a better solution, possibly making use of handlers(but I can't think of it offhand)) With a service you will have slight time delays from when A is telling Service that X is updated to when B can get that info. but its the best I can think of off the top of my head right now.
Also the SQLite DB is designed to promote data storage like this, and has built in support for monitors to block access to the DB when another thread is accessing it. (there is nuance here I won't get into, such as 'setLockingEnabled(boolean)')
Make it static on the top class
All the activity can call it. Like
MyObject hero = topActivity.Hero
but but but
I'm not sure at all it is the correct way or the best way to do it. So maybe wait for other response
Sincerely
A possibility is:
From Activity1 launch the Activity2 using the method startActivityForResult(). In the Activity2 you will do all the modification needed to the object "hero" and then you can put it in an intent and send it back using setResult (int resultCode, Intent data). This way, when Activity1 is resumed, using onActivityResult(int requestCode, int resultCode, Intent data), you will be able to catch the object "hero" modified.
Here you can find more information on this mechanism.
http://developer.android.com/training/basics/intents/result.html
You could write object data in shared prefs. That way there is no need for a service. You need to save the progress of the game anyway. I think changes like that should be saved in memory once the app closes the progress is back to where it was.
Edit:
Or you could save in Internal Storage. Save the Protag object as a file.
It all depends how often the object changes.
Tip: You should not be writing every time you make a change to the object. Save the data in some data structure and then when everything is finalized such as the level ends or something then you write in storage or shared prefs. That way you keep it efficient otherwise all that writing in storage will slow the app down.

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 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.

Categories