Adding data to a parcelable object passed to another activity - java

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.

Related

Passing references to Activity Intent

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.

How can I access a non-static method that requires some informations?

In my case, I have two classes MenuActivity, MinhaEscolaActivity.
There is a method in MenuActivity called quemSouEu, it is a non static method.
It also needs some properties defined on MinhaEscolaActivity's constructor method.
If I instanciate a new object of MinhaEscolaActivity on MenuActivity, these properties will be null, and i'll get NullPointerException.
Is there a way to use the method quemSouEu from MenuActivity class?
You shouldnt create an activity object by yourself. An activity is a main android component that is meant to be created by the system.
If you have some functions to be shared between activities, you should create another class, and instantiate an object in your activity.
If you also have data to share, you can think about some of the standar ways of sharing data, as you can read in this answer
What are the objects that you are creating on the second class constructor? you could start the activity and get the result, but youshould only do this if actually you need to show a new view or interact with the user in a diferent way, you shouldnt tell the system to run a new activity just for calling a function.
You can use intents to pass values between activities. You should never create an instance of activity class. Take a look at Raghav Sood's answer in the below post
Can i Create the object of a activity in other class?
Your storage options
http://developer.android.com/guide/topics/data/data-storage.html
Store the data and retrieve it when you need. Read the docs before choosing one.
You can pass data from MinhaEscolaActivity to MenuActivtiyusing intents and execute the method in MenuActivity it self

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.

Identifying explicit intent purpose in Android, or building seperate activities?

This may be in the RTFM category but I can’t seem to figure out the proper way to do this. One of my activities shows some random data from a database, it uses some user-defined search-criteria from a previous activity to filter out which data blocks to search from. But it’s main purpose is to display the data, and present the user with a UI to manipulate the data to his will.
The user can also bookmark this random data, and then access it later again (the bookmarks show up in a listview in another activity). Rather than creating a whole new activity with basically the same purpose, I want to reuse the one already created, and just tell it that I want to view some data, rather than search for some new. So what is the proper way of informing an activity of want you want to do? Should that be defined in the Intent extras bundle or is there another way?
Or would the proper way be to create a new activity for this?
You can extend the first Activity like this:
ActivityB extends ActivityA
and then the methods that need to be different in ActivityB can #override the methods in ActivityA, but methods that do the same thing, you don't have to dupe as long as they're protected.

Categories