Below is the example of my Parcelable class. As you can see, I want to put activity in a Parcel, but how could I do that? I look into the source code of Activity, it is not Parcelable nor Serializable.
public class MyParcelable implements Parcelable {
private Activity mActivity;
private int mData;
private String mName;
#Override
public void writeToParcel(Parcel out, int flags) {
//out.writeX(mActivity);
out.writeInt(mData);
out.writeString(mName);
}
private MyParcelable(Parcel in) {
//mActivity = in.readX();
mData = in.readInt();
mName = in.readString();
}
public MyParcelable(Activity activity, int data, String name) {
mActivity = activity;
mData = data;
mName = name;
}
#Override
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
#Override
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
#Override
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
}
You can put anything in Parcel and take anything out of Parcel. But whether that "anything" will work afterwards will depend on variety of factors.
All Activity instances exist in close cooperation with Android's ActivityManager. Most of methods, that define Activity: startActivity(), finish(), restart(), setContentView(), getPackage() delegate to methods of ActivityManager, ViewManager, PackageManager, and other system services. These methods won't work after Activity is destroyed by system. Those methods also won't work if you instantiate Activity class without Android's "special sauce". While it is technically possible to instantiate an Activity class using reflection or JNI, those instances simple won't work.
This is what people mean, when they say that Activity is "managed by Android": it is basically driven by OS processes, and can not exist without communication with those processes. Fortunately, Android has a way to initiate such communication: an Intent. Replace this line:
private Activity mActivity;
with
private Intent mActivity;
And use the parcelized Intent to start the Activity once you receive it on other side. Of course, if you need more things than just Intent to get your Activity in usable shape, you will have to store those things alongside with Intent within your Parcelable.
This is not good idea to put activity to parcel. If you require to do this, probably your code is bad and you need to review the code to avoid this implementation.
Related
I have a simple doubt in marshaling during service creation. When there is a writeToParcel() method declared in Parcelable interface which is invoked in stub generated (if aidl method parameters are declared as in), why there is no readFromParcel() declaration in Parcelable interface(for out parameters)?
I can create my own readFromParcel() but as per my understanding there should be a overridden readFromParcel() declaration in Parcelable interface if the generated stub wants to invoke it. But the documentation for Parcelable interface does not show any sign of readFromParcel() method. Why is it so? Was it included in previous API version and later got removed? Please explain !
And how different is createFromParcel() from readFromParcel() if both tries to read a parcelable object and populate member fields with the data out of it?
This is because you have declared a parameter of that type as "inout" in your AIDL.
When returned from the method, generated AIDL proxy will call readFromParcel() to update the parameter value (as defined by the "inout" qualifier).
createFromParcel is exactly what it sounds like. A NEW Intance of the parcelable Object/Class that has been written to parcel : Parcelable.writeToParcel() is created. This is a good thing, as it helps prevent memory leaks, as you are not holding on to a reference to the object from another class that may or may not have been destroyed
From the documentation of Parcelable interface :
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}
When an object of a class which implements the Parcelable interface is to be written to a Parcel, writeToParcel(...) is called.
When an object of this class is to be created using a Parcel, CREATOR.createFromParcel(Parcel in) is called. From there onwards, how the class creates its instance from the Parcel is upto the developer of the class. In the above example, a constructor private MyParcelable(Parcel in) is called from the createFromParcel(...) method.
Conventionally, many developers define a readFromParcel(Parcel in) method in their implementations and call it from the constructor:
private MyParcelable(Parcel in) {
readFromParcel(in);
}
private void readFromParcel(Parcel in) {
mData = in.readInt();
}
I cant resolve problem when i sending my object "filmovi" to another activity i got a error. when i was tried to send another object "korisnik" it works without any problem.
Error
FATAL EXCEPTION: main
java.lang.ClassCastException: ba.fit.kino.model.filmovi cannot be cast to android.os.Parcelable
Sending from activity
filmovi Film = ((filmovi)lstView.getItemAtPosition(position));
Intent intent = new Intent(getApplicationContext(), RezervacijaActivity.class)
intent.putExtra("Rezervacija", Film);
startActivity(intent);
Reciving in activity
filmovi filmoviRezervacija;
Bundle bundle = getIntent().getExtras();
if(bundle != null){
filmoviRezervacija = bundle.getParcelable.("Rezervacija");
}
I RESOLVE PROBLEM WITHT THIS:
public class filmovi implements Parcelable{......
public filmovi (Parcel source)
{
this.setFilmID(source.readInt());
this.setNaziv(source.readString());
this.setCijenaKarte(source.readFloat());
this.setSalaID(source.readInt());
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest,int flags)
{
dest.writeInt(this.filmID);
dest.writeString(this.naziv);
dest.writeFloat(this.cijenaKarte);
dest.writeInt(this.salaID);
}
public static final Parcelable.Creator<filmovi> CREATOR = new Parcelable.Creator<filmovi>() {
#Override
public filmovi createFromParcel(Parcel source) {
return new filmovi(source);
}
#Override
public filmovi[] newArray(int size) {
return new filmovi[size];
}
};
}
The reason is that your filmovi class is not parcelable
To make filmovi, or any class for that matter, parcelable, the class and all of its inner members should
implement the parcelable interface, and implement a writeToParcel method which loosely speaking
streams the class' content.
Here, for example
class MyClass implements parcelable {
private MyMemberDataClass data; <----- must also implement parcelable
void writeToParcel(Parcel dest, int flags) {...}
}
It is not enough that MyClass will implement parcelable.
MyMemberDataClass (i.e. the inner member class) must do so as well.
This may bet complicated. And in many cases it is also not really necessary...
instead, consider using an activity-parameters static object to which you will pass all of
your activity's required params without the need to parcel them!:
filmovi Film = ((filmovi)lstView.getItemAtPosition(position));
Intent intent = new Intent(getApplicationContext(), RezervacijaActivity.class)
RezervacijaActivityParams.setValue(Film); <--------------- instead of putExtra()
startActivity(intent);
Where:
class RezervacijaActivityParams {
private static filmovi Film;
public static void getValue(filmovi f) { Film = f; }
public static filmovi getValue() { return Film; }
}
and in RezervacijaActivity's onCreate:
class RezervacijaActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
filmovi = RezervacijaActivityParams.getValue();
RezervacijaActivityParams.setValue(null); <---------- clear static data
}
}
Note, and this is also an answer to kcoppock's comment, that it is a good practice for
your activity to clear the static data immediately after retrieving it.
EDIT: As kcoppock mentioned, you can't place Objects into Intents as extras unless they're serializable or parcelable. Therefore, your Film class will need to implement one of those. I've only ever added native types (int, etc.) to Intents, so I did not know this. Something to watch out for!
As an aside, it's good practice to extract your key strings to static final values. That ensures that the same string ends up in each location you use it.
I'm asking this question: instread of giving a string, a int and so on, can we push a custom object during the creation fo a new Intent?
newActivity.PutExtra("JsonDataResult", business.getJSON());
In fact I have one object constructed thanks to a JSON (from webrequest) , I parse it and I put it on an object.
At this point I'm passing the string returned from the webrequest to another intent but the parsing takes a long time tu be done, so it could be super-cool the ability to pass custom object with intent.
EDIT : I'm using monodroid / xamarin, so
Android.OS.IParcelable cannot be implemented,
Java.IO.ISerializable cannot be implemented.
You can either let your custom classes implement Parcelable (Google says its faster, but you have to do more coding) or Serializable.
Then add your objects to a bundle (or to the "extra"):
Bundle b = new Bundle()
b.putParcelable("myObject",myObject);
b.putSerializable("myObject",myObject);
For info to Parcelablecheckout this
And if you're interested in the difference between Parcelable and Serializable in more detail check out this
I personally prefer the usage of Serializable for simple object-passing, since the code ist not spoiled with so much code.
Edit: ok isn't your question very similar to this then?
As you've specified you're using Monodroid, it looks like it's not straightforward. I did a quick search and found this forum post
Which listed the following solutions to this problem in Monodroid:
Store the custom Object to be passed as a global variable somewhere, and just read it from your second activity
Which is a bit messy and bad practice, but would work.
Or
serialize your class to a string and send the string to the second Activity
Which will be a little more hard work, but better practice
This is an example how to create a Parcelable class:
public class Person implements Parcelable {
private String name;
private String surname;
private String email;
// Get and Set methods
#Override
public int describeContents() {
return hashCode();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(surname);
dest.writeString(email);
}
// We reconstruct the object reading from the Parcel data
public Person(Parcel p) {
name = p.readString();
surname = p.readString();
email = p.readString();
}
public Person() {}
// We need to add a Creator
public static final Parcelable.Creator<person> CREATOR = new Parcelable.Creator<person>() {
#Override
public Person createFromParcel(Parcel parcel) {
return new Person(parcel);
}
#Override
public Person[] newArray(int size) {
return new Person[size];
}
};
Give a look here if you want to use Parcelable.
My program has a range of different class activities (basically different screens). In one activity I am creating multiple objects which I would then like to access in other activities.
How do I go about making these objects accessible to other activities within my program, in other words how do I share objects with other activities?
TIA
Mark
The first thing you need to resolve is the operation order. If activity A is the one with the shared objects, what would you do if activity B is run without activity A ever being initialized? Do remember that intents to start activities may come from everywhere, though, to be truthful, exiting with NULL pointer dereference is an acceptable response.
What I did when such a thing was necessary was to not have the shared objects part of the activity, but create a specific object for containing those. You can then store a static reference to that object inside the object, and return it via a static method:
public class GlobalParams {
private static reference;
public static GlobalParams getReference()
{
if( reference==NULL )
reference=new GlobalParams();
return reference;
}
}
I don't think parcelable would help you, as that would create distinct copies for the different Activities to use.
Shachar
You need to have that class implement Parcelable
It's basically kinda similar to Java's serializable. You have to tell your class how to pack and unpack itself. Then you can just put it in an intent via intent.putExtra();
Here is the code example taken from that link
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}
I'd like for my data class to implement Parcelable so it can be shared between Activities, however it also needs reference to Context so the fields can be saved to SQLiteDatabase.
This however is a problem since Parcelable.Creator method createFromParcel only has one parameter Parcel.
public abstract class Record implements Parcelable {
protected Context context;
protected String value;
public Record(Context context) {
this.context = context;
}
public Record(Parcel parcel) {
this.value = parcel.readString();
}
public void save() {
//save to SQLiteDatabase which requires Context
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flag) {
parcel.writeString(value);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Record createFromParcel(Parcel parcel) {
return new Record(in);
}
public Record[] newArray(int size) {
return new Record[size];
}
};
}
How can a class that implements Parcelable also reference Context so it save to SQLiteDatabase?
The Parcelable interface is like the Java interface Serializable. Objects which implement this interface should be serializable. This means it should be possible to transform the object to a representation which could be saved in a file e.g.
It is easily possible for a string, int, float or double etc, because they all have a string representation. The Context class is clearly not serializable and not parcelable, because it can be an Activity for example.
If you want to save the state of your activity to a database, you should find another way to do that.
Your Record class probably doesn't really need access to the SQL database. The reason for it is exactly the problem you have now: it's very difficult to inject the Context back into each Record.
Perhaps a better solution would be to implement a static RecordSQLService, that has method save(Record r). Your app could start RecordSQLService when the app launches, so it will remain alive as long as your app does, and it takes the responsibility of saving away from the Record class, which makes it so you don't need Context anymore and can Parcel it.