Android passing Bundle with primitive and Parcelable - android

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.

Related

Android putSerializable vs putString

i have a POJO object defined like this:
public class MYPOJO implements Serializable {
int myint;
String mystring;
}
then when im passing the info to an activity i do this:
Intent i = new Intent(this, WearActivity.class);
Bundle b = new Bundle();
MYPojo pojo = new MYPojo();
pojo.mystring="cool";
b.putSerializable("someString", pojo.mystring);//this line is my issue
i.putExtra("coolBundle",b);
startActivityForResult(i, 0);
and to read the 'someString' extra i do:
Bundle b2 = getIntent().getBundleExtra("coolBundle");
b2.getString("someString");
So now onto my question: is there really any difference if i do the following if if at the end im still calling for retrieval b2.getString("someString") :
b.putString("someString",pojo.mystring)
vs
b.putSerializable("someString",pojo.mystring) ?
In practice, there isn't a difference you'll see between passing a string, or passing a serializable string between activities, besides "efficiency" or possible usage of reflection/security. It might be more of a design and principle issue I have with passing a String in as "serializable". Essentially by passing in a String as Serializable, Android/Java will utilize the wrapper of String, which contains a data structure of char[] behind the scenes.
TLDR; If your POJO contained more data, I would highly recommend implementing Parcelable. More information can be found in this SO answer here. But if you are going to be passing just a String, I would use the putString/getString methods that Android provides for us.

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

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.

What does bundle mean in Android? [duplicate]

This question already has answers here:
What is a "bundle" in an Android application
(12 answers)
Closed 7 years ago.
I am new to android application development, and can't understand what does bundle actually do for us.
Can anyone explain it for me?
I am new to android application development and can't understand what
does bundle actually do for us. Can anyone explain it for me?
In my own words you can image it like a MAP that stores primitive datatypes and objects as couple key-value
Bundle is most often used for passing data through various Activities. Provides putType() and getType() methods for storing and retrieving data from it.
Also Bundle as parameter of onCreate() Activity's life-cycle method can be used when you want to save data when device orientation is changed (in this case activity is destroyed and created again with non null parameter as Bundle).
More about Bundle at its methods you can read reference at developer.android.com where you should start and then make some demo applications to get experience.
Demonstration examples of usage:
Passing primitive datatypes through Activities:
Intent i = new Intent(ActivityContext, TargetActivity.class);
Bundle dataMap = new Bundle();
dataMap.putString("key", "value");
dataMap.putInt("key", 1);
i.putExtras(dataMap);
startActivity(i);
Passing List of values through Activities:
Bundle dataMap = new Bundle();
ArrayList<String> s = new ArrayList<String>();
s.add("Hello");
dataMap.putStringArrayList("key", s); // also Integer and CharSequence
i.putExtras(dataMap);
startActivity(i);
Passing Serialized objects through Activities:
public class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private ArrayList<FooObject> foos;
public Foo(ArrayList<FooObject> foos) {
this.foos = foos;
}
public ArrayList<FooObject> getFoos() {
return this.foos;
}
}
public class FooObject implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
public FooObject(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
Then:
Bundle dataMap = new Bundle();
ArrayList<FooObject> foos = new ArrayList<FooObject>();
foos.add(new FooObject(1));
dataMap.putSerializable("key", new Foo(foos));
Pass Parcelable objects through Activities:
There is much more code so here is article how to do it:
Parcel data to pass between Activities using Parcelable classes
How to retrieve data in target Activity:
There is one magic method: getIntent() that returns Intent (if there are any data also with extended data) that started Activity from there method is called.
So:
Bundle dataFromIntent = getIntent().getExtras();
if (dataFromIntent != null) {
String stringValue = dataFromIntent.getString("key");
int intValue = dataFromIntent.getInt("key");
Foo fooObject = (Foo) dataFromIntent.getSerializable("key");
// getSerializble returns Serializable so we need to cast to appropriate object.
ArrayList<String> stringArray = dataFromIntent.getStringArrayList("key");
}
Usage of Bundle as parameter of onCreate() method:
You are storing data in onSaveInstanceState() method as below:
#Override
public void onSaveInstanceState(Bundle map) {
map.putString("key", "value");
map.putInt("key", 1);
}
And restore them in onCreate() method (in this case is Bundle as parameter not null) as below:
#Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
String stringValue = savedInstanceState.getString("key");
int intValue = savedInstanceState.getString("key");
}
...
}
Note: You can restore data also in onRestoreInstanceState() method but it's not common (its called after onStart() method and onCreate() is called before).
In general english: "It is a collection of things, or a quantity of material, tied or wrapped up together."
same way in Android "It is a collection of keys and its values, which are used to store some sort of data in it."
Bundle is generally used for passing data between various component. Bundle class which can be retrieved from the intent via the getExtras() method.
You can also add data directly to the Bundle via the overloaded putExtra() methods of the Intent objects. Extras are key/value pairs, the key is always of type String. As value you can use the primitive data types.
The receiving component can access this information via the getAction() and getData() methods on the Intent object. This Intent object can be retrieved via the getIntent() method. And
the component which receives the intent can use the getIntent().getExtras() method call to get the extra data.
MainActivity
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString(“Key“, myValue);
intent.putExtras(bundle);
startActivity(intent);
SecondActivity
Bundle bundle = getIntent().getExtras();
String myValue= bundle.getString(“key“);
A collection of things, or a quantity of material, tied or wrapped up together. it is the dictionary meaning...By the same Bundle is a collection of data. The data may be of any type i.e String, int,float , boolean and any serializable data. We can share& save the data of one Activity to another using the bundle Bundle.
Consider it as a Bundle of data, used while passing data from one Activity to another.
The documentation defines it as
"A mapping from String values to various Parcelable types."
You can put data inside the Bundle and then pass this Bundle across several activities. This is handy because you don't need to pass individual data. You put all the data in the Bundle and then just pass the Bundle, instead of sending the data individually.
It's literally a bundle of things; information: You put stuff in there (Strings, Integers, etc), and you pass them as a single parameter (the bundle) when use an intent for instance.
Then your target (activity) can get them out again and read the ID, mode, setting etc.
A mapping from String values to various Parcelable types.Click here
Example:
Intent mIntent = new Intent(this, Example.class);
Bundle mBundle = new Bundle();
mBundle.extras.putString(key, value);
mIntent.putExtras(mBundle);
Send value from one activity to another activity.

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

Bundle of array of arraylist

How can I put an array of arrayList into a Bundle?
ArrayList < myObjects >[] mElements;
Make YourObject implement the Parcelable interface, then use bundle.putParcelableArraylist(theParcelableArraylist).
Edit: whoops misread the question. Why do you want to save an array of arraylist? If its for persisting in an orientation change, maybe you want to use onRetainNonConfigurationInstance instead?
Edit 2: Ok, here goes. Create a new Wrapper class that implements Parcelable (note that myObjects also have to be parcelable). The writeToParcel method will look something like this:
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mElements.length);
for(ArrayList<MyObject> element : mElements) {
dest.writeParcelableArray(element.toArray(new MyObject[0]), flags);
}
}
And the constructor:
private Wrapper(Parcel in) {
int length = in.readInt();
//Declare list
for (int i = 0; i < length; i++) {
MyObject[] read = in.readParcelableArray(Wrapper.class.getClassLoader());
//add to list
}
}
Not possible using bundle, as bundle allows arraylist of primitives only...
Try to use parcleable or application level data or static variable (bad practice).
If your objects support serialization, marked with the Serializable interface, then you should be able to use bundle.putSerializable.
ArrayList supports Serializable , but I'm not sure about a plain array.
I just use putSerializable(myarraylistofstuff) and then I get back with a cast using get(), you just need to silence the unchecked warning. I suspect (correct me if wrong) you can pass any object faking it as Serializable, as long you stay in the same process it will pass the object reference. This approach obviously does not work when passing data to another application.
EDIT: Currently android passes the reference only between fragment, I've tried to pass an arbitrary object to an Activity, it worked but the object was different, same test using arguments of Fragment showed same Object instead.
Anyway it deserializes and serializes fine my Object, if you have a lot of objects it's better to use Parcel instead

Categories

Resources