Android Message.getData().getParcelable() doesn't create new object - android

I always thought that when I am passing custom parcelable object, by calling getParcelable(), it will return a totally new object. This is true when I am passing a parcelable object from one Activity to another Activity by putting that parcelable in the Bundle (Bundle is put in Intent).
But when I am trying to pass a parcelable object from a Activity to a background thread which has a Handler attach to, I don't get a new object which means if I modify the value of that object in the background thread, it will affect the object in Activity as well. How come? What have I done wrong?
This time the object is put in Bundle but the Bundle is put in Message, this is the only difference I can find when compare this to passing Parcelable between Activity.
//simple code sample, overwrite handleMessage() of Handler
public void handleMessage(Message msg) {
SkCounter sc = msg.getData().getParcelable("abc");
sc.testing = 99999; //if I change this value here, the object's value from Activity will change too. getParcelable() should create totally new object and changing value of it shouldn't affect other object, right? But how come this happens?
}
Anyone knows why?

When passing a Bundle object from Activity to another Activity, Android framework will parcel and unparcel your object, the unparcel process will create a new object;
When passing a Bundle object through Message, Message.setData() just set the object, without doing parcel, so you are accessing the same object.
you can check Bundle source, your data is stored either in a Map or Parcel:
// Invariant - exactly one of mMap / mParcelledData will be null
// (except inside a call to unparcel)
/* package */ Map<String, Object> mMap = null;
/*
* If mParcelledData is non-null, then mMap will be null and the
* data are stored as a Parcel containing a Bundle. When the data
* are unparcelled, mParcelledData willbe set to null.
*/
/* package */ Parcel mParcelledData = null;
the Bundle.unparcel() method recreates your objects from Parcel.

Related

Android passing Bundle with primitive and Parcelable

I don't understand why I cannot Bundle both a Parcelable object and a primitive in the same Bundle. See below.
Bundle bundle = new Bundle();
bundle.putInt("num", 4); // I like 4.
bundle.putParcelable("myObject", myObject);
Intent localIntent = new Intent(SendingClass.this, ReceivingClass.class);
localIntent.putExtras(bundle);
startActivity(localIntent);
And my ReceivingClass' onCreate(Bundle savedInstanceState), (which i'm aware that the sIS is null here)...
Bundle bundle = getIntent().getExtras();
int num = bundle.getInt("num");
MyObject myObject = (MyObject) bundle.getParcelable("myObject");
I would have expected both num and myObject to contain something, however, myObject is populated, but num is always 0.
If I remove the putParcelable and getParcelable lines however, "num" is then correctly populated (with 4).
Looking at the bundle, it seems to only populate with a Parcelable object if it has one, and "drops" all other primitives. Why is this? I haven't been able to find documentation saying Bundles cannot contain both primitives and Parcelables, what gives?
P.S. I have tried to add both the primitive to the Bundle first, and after the Parcelable, but no dice either way.
As expected, it was due to an error in my Parcelable class.
If your public void writeToParcel(Parcel paramParcel, int paramInt){ method writes too many things for the "make object from Parcel' method public ParcelableClass(Parcel paramParcel) {, you will get strange errors in your log, this is what is causing the issue.
Make sure the read and write methods have the same number of read/writes, and of the correct data types.
In my case I was writing 4 objects, (2 Parcelable, 2 primitives), but only reading the 2 Parcelables, and 1 primitive. It just seems strange that some objects made it through ok.

Android do i need to set each object to null when done using it?

I have this object I've created which holds references to some other objects:
public class ListHandler {
private AppVariables app; //AppVariables instance
private Extra extra; //the extra argument represanting the list
private ArrayList<FacebookUser> arrayList; //the array list associate with the list given
private Comparator<FacebookUser> comparator; //the comparator of the list
private String emptyText; //list empty text
/**
* Constructor - initialize a new instance of the listHandler
* #param app the current {#link AppVariables} instance
* #param extra the {#link Extra} {#link Enum} of the list
*/
public ListHandler(AppVariables app, Extra extra)
{
this.app = app;
this.extra = extra;
//set the array list to match the list given in the arguments
setArrayList();
setComparator();
setEmptyTest();
}
/**
* Clear all resources being held by this object
*/
public void clearListHandler()
{
this.arrayList = null;
this.comparator = null;
this.app = null;
this.emptyText = null;
this.extra = null;
}
I've built the clearListHandler() method in order to set all the objects I'm using to null when finished using the ListHandler.
Is it necessary? Do I need to clear all the objects in order to get them garbage collected later or will the GC know that this object is no longer in use since the object which initialized it is no longer in use?
Garbage Collection is pretty smart, and you generally don't need to explicitly set objects to null (though it helps in some cases when using Bitmaps).
An Object becomes eligible for Garbage collection or GC if its not reachable from any live threads or any static refrences in other words you can say that an object becomes eligible for garbage collection if its all references are null. Cyclic dependencies are not counted as reference so if Object A has reference of object B and object B has reference of Object A and they don't have any other live reference then both Objects A and B will be eligible for Garbage collection.
Generally an object becomes eligible for garbage collection in Java in following cases:
All references of that object explicitly set to null e.g. object = null
Object is created inside a block and reference goes out scope once control exit that block.
Parent object set to null, if an object holds reference of another object and when you set container object's reference null, child or contained object automatically becomes eligible for garbage collection.
If an object has only live references via WeakHashMap it will be eligible for garbage collection.
You can find more details on Garbage Collection here.
You shouldn't do that. The garbage collector will determine automatically when its best to clear all the objects.
Try reading this and this.
No. The Dalvik/Java Virtual Machine will allocate memory, and de-allocate memory as need be.
There is no problem with what you are doing, it's just unnecessary.

Passing a custom Object from one Activity to another Parcelable vs Bundle

I'd like to pass a custom Object from one activity to another, the Object consists of a String and a List of another custom Object which consists of an array of strings and an array of ints. I've read https://stackoverflow.com/a/2141166/830104, but then I've found this answer https://stackoverflow.com/a/7842273/830104. Which is better to use Bundle or Parcelable? What is the difference? When should I use this each? Thanks for your replies, Dan
Parcelable and Bundle are not exclusive concepts; you can even deploy both on your app at a time.
[1] Term Parcelable comes with Serialization concept in Java (and other high-level language such as C#, Python,...). It ensures that an object - which remains in RAM store - of such Parcelable class can be saved in file stream such as text or memory (offline status) then can be reconstructed to be used in program at runtime (online status).
In an Android application, within 2 independent activities (exclusively running - one starts then other will have to stop):
There will be NO pointer from current activity to refer to previous one and its members - because previous activity is stopped and cleared out form memory; so that to maintain object's value passed to next activity (called from Intent) the object need to be parcelable (serializable).
[2] While Bundle is normally the Android concept, denotes that a variable or group of variables. If look into lower level, it can be considered as HashMap with key-value pairs.
Conclusion:
Bundle is to store many objects with related keys, it can save any object in native types, but it doesn't know how to save a complex object (which contains an ArrayList for example)
Parcelable class is to ensure a complex instance of it can be serialized and de-serialized during runtime. This object can contains complex types such as ArrayList, HashMap, array, or struct,...
[UPDATED] - Example:
//Class without implementing Parcelable will cause error
//if passing though activities via Intent
public class NoneParcelable
{
private ArrayList<String> nameList = new ArrayList<String>();
public NoneParcelable()
{
nameList.add("abc");
nameList.add("xyz");
}
}
//Parcelable Class's objects can be exchanged
public class GoodParcelable implements Parcelable
{
private ArrayList<String> nameList = new ArrayList<String>();
public GoodParcelable()
{
nameList.add("Can");
nameList.add("be parsed");
}
#Override
public int describeContents()
{
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags)
{
// Serialize ArrayList name here
}
}
In source activity:
NoneParcelable nonePcl = new NoneParcelable();
GoodParcelable goodPcl = new GoodParcelable();
int count = 100;
Intent i = new Intent(...);
i.putExtra("NONE_P",nonePcl);
i.putExtra("GOOD_P",goodPcl);
i.putExtra("COUNT", count);
In destination activity:
Intent i = getIntent();
//this is BAD:
NoneParcelable nP = (NoneParcelable)i.getExtra("NONE_P"); //BAD code
//these are OK:
int count = (int)i.getExtra("COUNT");//OK
GoodParcelable myParcelableObject=(GoodParcelable)i.getParcelableExtra("GOOD_P");// OK

getParcelable() returns null object

I've used putExtra() to pass some data from one activity to other. I want to create a Parcelable instance but I am getting a null object.
Here is the code of first activity:
i = new Intent(Activity1.this, Activity2.class);
i.putExtra(com.login_app.Activity1.extra, "100");
startActivity(i);
Here is the code of second activity:
Intent inew = getIntent();
Bundle icicle1 = inew.getExtras();
// this is just a debug code
System.out.println(
icicle1.getSerializable(com.login_app.Activity1.extra).toString());
Parcelable p = inew.getParcelableExtra(com.login_app.Activity1.extra);
Here object p is a null object.
Please tell me if I am wrong or I need to add something else. I want this Parcelable object to be flattened into a Parcel object.
That's because you've put String, but trying to get Parcelable. You should use getStringExtra instead.
Also, from Bundle documentation of [getParcelable()][1] (this function is used to actually get extra from Intent's bundle):
Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key.
So you basically get null because you have type mismatch.

Passing data using Intents

I have two activities, Activity A and Activity B. I pass objects from Activity A to Activity B using intents. When i make changes to the Object in Activity B the data changes does not get reflected in Activity A. Have i missed out on something?
You are missing the fact that when you pass Object O from Activity A to Activity B via intents, activity B receives a COPY of object O. The way things work is that The object O gets serialized (converted to a sequence of bytes) and that sequence of bytes is then passed to Activity B. Then activity B recreates a copy of object O at the moment it was serialized. Any changes to the original object after it was serialized are not reflected in it's copy.
If both activities are part of the same application then just use a static field (or singleton) to communicate between them.
If you are passing a String, then it will not change since they are immutable.
Edit: See below for an alternative to Intent extras.
If you wish to use the architecture of passing immutable objects in messages you can create an immutable serializable data class. Pass an immutable instance of the data class in the intent using startActivityForResult. When the second activity is completed, send a different instance of the same immutable data class back to the first activity where it is trapped in onActivityResult. So in code, given an immutable class PasswordState.java with public final fields.
public final class PasswordState implements Serializable {
Create an instance of this immutable class and send it to the second activity as in:
private void launchManagePassword() {
Intent i= new Intent(this, ManagePassword.class); // no param constructor
PasswordState outState= new PasswordState(lengthKey,
timeExpire,
isValidKey,
timeoutType,
password,
model.getIsHashPassword(),
model.getMinimumPasswordLength()); // NOT minimumHashedPasswordLength
Bundle b= new Bundle();
b.putSerializable("jalcomputing.confusetext.PasswordState", outState);
i.putExtras(b);
startActivityForResult(i,REQUEST_MANAGE_PASSWORD); // used for callback
}
The second activity returns a new object when it is done.
PasswordState outPasswordState= new PasswordState(lengthKey,
timeExpire,
isValidKey,
timeoutType,
password,
isHashPassword,
minimumPasswordLength);
Bundle b= new Bundle();
b.putSerializable("jalcomputing.confusetext.PasswordState", outPasswordState);
getIntent().putExtras(b);
setResult(RESULT_OK,getIntent()); // call home with data on success only
finish(); // go back <=== EXITS Here
Where it is trapped in Activity one.
// ON_ACTIVITY_RESULT used for callback from ManageKeys.java
protected void onActivityResult(int request, int result, Intent data)
{
switch (request){
case REQUEST_MANAGE_PASSWORD:
if (result == RESULT_OK) { // Success. New password.
try {
PasswordState inMessage= (PasswordState)data.getSerializableExtra("jalcomputing.confusetext.PasswordState");
password= inMessage.password;
timeExpire= inMessage.timeExpire;
isValidKey= true;
writeToPrefs(); // support first launch and incoming tagged sms, save pw
}
catch(Exception e){ // data == null, extras == null
password= "";
isValidKey= false;
timeExpire= PasswordState.LONG_YEAR_MILLIS;
Log.d(Utilities.TAG,"FailedToGetResult",e); // will be stripped at runtime
}
...
break;
}
}
When you are done prototyping and the data objects are stable, you can refactor the code to use parcels instead of serializing objects. Since a copy is being sent between activities using serialization, it could be argued that the use of an immutable object is overkill. Using a mutable object and serializing a mutable object to and from the second activity would simplify the implementation.
Hope that helps.
JAL

Categories

Resources