aattss
aattss

Reputation: 49

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?

Upvotes: 3

Views: 128

Answers (4)

mawalker
mawalker

Reputation: 2070

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)')

Upvotes: 2

Arnold Balliu
Arnold Balliu

Reputation: 1119

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.

Upvotes: 0

Roberto Lombardini
Roberto Lombardini

Reputation: 616

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

Upvotes: 0

kevingiroux
kevingiroux

Reputation: 155

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

Upvotes: 0

Related Questions