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).
Related
Lately I've read this comment on Reddit:
I think EventBus on android is popular because people don't know how to share a java object reference between android components like a Fragment and an Activity, or 2 Activities and so on. So basically I think people don't know how 2 Activites can observe the same object for data changes which I think comes from the fact that we still don't know how to architect our apps properly.
As far as I know:
directly reference values
((HostActivity)getActivity()).someValue
may cause problem such as memory leaks or NullPointerException
Callbacks or other observer pattern
EventBus,inner class like listener and etc
So I was curious that whether there is any other ways , could you share with me on it?
imo, there are few ways out of your opinion such as:
use local db, now ORM like Realm is really popular, its also safe way.
use share preferences if your object is able to convert to json, its up to your situation.
use intent with serialize/parcelable object bundle.
use a singleton, now #scope with Dagger 2 is good choice.
use RxJava as event bus.
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.
For quite some time I've had troubles passing variables from one Activity to another, and I've usually had to resolve to some pretty ugly Static-class-hacks to make it work.
Generally something along the lines of a static method that I call with the type of the Activity, along with the variables the Activity requires. These gets stored in a static variable, and retrieved in the constructor of said activity.
Like I said, pretty ugly. And there's no such thing as "myActivity.StartActivity(new Activity);". All of the overloads for StartActivity takes either an Intent, or a typeof(MyOtherActivity).
So my question is, have I completely misunderstood the concept of Activities, or am I simply missing a completely obvious way to pass arguments to them?
#Edit: The reason I want to pass an actual reference to an object, instead of simply a copy of the object, is because I'm trying to pass a View Model from an overlying Activity, down to the new Activity. And of course any changes made to this view model, should be reflected on the parent activity, which will only be possible if the the two activy's viewmodels points to the same instance.
I'm writing the app using Xamarin.Android, but the code is nearly identical between C# and Java, so answers in either those languages is fine.
The problem is that Android can kill the process hosting your app at any time (if it is in the background). When the user then returns to your app, Android will create a new process to host your app and will recreate the Activity at the top of the stack. In order to do this, Android keeps a "serialized" version of the Intent so that it can recreate the Intent to pass it to the Activity. This is why all "extras" in an Intent need to be Parcelable or Serializable.
This is also why you cannot pass a reference to an object. When Android recreates the process, none of these objects will exist anymore.
Another point to consider is that different activities may run in different processes. Even activities from the same application may be in different processes (if the manifest specifies this). Since object references don't work across process boundaries, this is another reason why you cannot pass a reference to an object in an Intent.
You can also use The Application class to store objects globally and retrieve them:
using Android.Runtime;
namespace SomeName
{
[Application]
public class App : Application
{
public string Name { get; set;}
public App (IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public override void OnCreate ()
{
base.OnCreate ();
Name = "";
}
}
}
And you can access the data with:
App application = (App)Application.Context;
application.Name = "something";
I choose to do this on the Application calss because this class is called on the App startup so you don't have to initiate it manually.
Keep in mind that variables which are scoped to the Application have their lifetime scoped to the application by extension.
This class will be Garbage Collected if the Android feels it is necessary so you have to modify the code to include this case also.
You can use SharedPreferences or a Database to save your variables in case they get deleted and retrieve them from the App class for faster results.
Don't be overly wasteful in how you use this approach though, as attaching too much information on this class it can lead to a degradation in performance. Only add information that you know will be needed by different parts of the application, and where the cost of retrieving that information exceeds the cost of storing it as an application variable.
Investigate which information you need to hold as application wide state, and what information can simply be retrieved straight from your database. There are cost implications for both and you need to ensure you get the balance right.
And don't forget to release resources as needed on OnStop and OnDestroy
I rarely use intents, i find this way better.
I need to make a model of cellphone with:
3 types of dates with appropriate name and date.
Set and get methods: at least 6.
At least 1 method defining typical behavior of the object.
Can you please tell me how to begin doing that. It's one of my first projects and I don't really know how to begin with this.
Any tips would be helpful I just dont know what is asked of me exactly and how to go about doing it.
Let's do it. You need to create a Java Class to model a cellphone.
1.- A `class` is a definition of a kind of object, which models an element.
So you're going to make a class to represent this cellphone.
2.- Variables store data.
You're asked to store 3 different kind of data about the cellphone. Just pick 3, what about the brand i.e.? You can use it and two more.
You will have to declare one variable inside your class to store each one of these values. About the apropriate name, it means to don't call that vars "var1" nor like this, but something with semantic meaning like "cell_brand", which gives you the meaning of it when you read the code.
3.- Properties: get and set
Classes let us encapsulate data and methods inside it. Then, you need to make this data accesible from the outside. This is achieved by get/set methods. The first one let you obtain the data from the property you define, and the second one to change it.
4.- Methods
Explained simple, a method is "a piece of code which you can ask to execute when you want, without needing to write it again". It operates over the object where it's contained.
Then, you need to write a method to simulate one behavior of the cell phone: a call, a message, etc.
5.- Do it
You will need to look for how to make these things on java. There're billions of tutorials on Internet, I'll leave here the first I found. Good luck
Java Classes
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.