Linkedlist Put into Intent Extra Gets Recast to Arraylist When Retrieving in Next Activity

LinkedList put into Intent extra gets recast to ArrayList when retrieving in next activity

I can tell you why this is happening, but you aren't going to like it ;-)

First a bit of background information:

Extras in an Intent are basically an Android Bundle which is basically a HashMap of key/value pairs. So when you do something like

intent.putExtra(AppConstants.KEY_ITEMS, items);

Android creates a new Bundle for the extras and adds a map entry to the Bundle where the key is AppConstants.KEY_ITEMS and the value is items (which is your LinkedList object).

This is all fine and good, and if you were to look at the extras bundle after your code executes you will find that it contains a LinkedList. Now comes the interesting part...

When you call startActivity() with the extras-containing Intent, Android needs to convert the extras from a map of key/value pairs into a byte stream. Basically it needs to serialize the Bundle. It needs to do that because it may start the activity in another process and in order to do that it needs to serialize/deserialize the objects in the Bundle so that it can recreate them in the new process. It also needs to do this because Android saves the contents of the Intent in some system tables so that it can regenerate the Intent if it needs to later.

In order to serialize the Bundle into a byte stream, it goes through the map in the bundle and gets each key/value pair. Then it takes each "value" (which is some kind of object) and tries to determine what kind of object it is so that it can serialize it in the most efficient way. To do this, it checks the object type against a list of known object types. The list of "known object types" contains things like Integer, Long, String, Map, Bundle and unfortunately also List. So if the object is a List (of which there are many different kinds, including LinkedList) it serializes it and marks it as an object of type List.

When the Bundle is deserialized, ie: when you do this:

LinkedList<Item> items = (LinkedList<Item>)
getIntent().getSerializableExtra(AppConstants.KEY_ITEMS);

it produces an ArrayList for all objects in the Bundle of type List.

There isn't really anything you can do to change this behaviour of Android. At least now you know why it does this.

Just so that you know: I actually wrote a small test program to verify this behaviour and I have looked at the source code for Parcel.writeValue(Object v) which is the method that gets called from Bundle when it converts the map into a byte stream.

Important Note: Since List is an interface this means that any class that implements List that you put into a Bundle will come out as an ArrayList.
It is also interesting that Map is also in the list of "known object types" which means that no matter what kind of Map object you put into a Bundle (for example TreeMap, SortedMap, or any class that implements the Map interface), you will always get a HashMap out of it.

how to pass the linkedList customizedType from Activity A to Activity B In android

You should use intent.putExtra("yourkey", linkedList);

In your B activity, just use getIntent().getExtras().get("key");
getExtras().get() suppose you will get an Object (like a LinkedList)

Hope helps

Why is the system automatically converting my LinkedHashMap to HashMap while passing it in an Intent as a Serializable?

The explanation for this is given here: LinkedList put into Intent extra gets recast to ArrayList when retrieving in next activity

If you want to recover a LinkedList, or a Map where the order of the keys is not important, you can use the solution of @schtever. If you want to recover a LinkedHashMap or a TreeMap and the order of the keys is important, it is a bit more tricky.

One possible solution is to convert the Map into a pair of Lists, as demonstrated below:

Activity1:

public class Activity1 extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Map<Integer, String> map = new LinkedHashMap<Integer, String>();
map.put(1, "ONE");
map.put(2, "TWO");
map.put(3, "THREE");

ArrayList<Integer> keys = new ArrayList<Integer>();
ArrayList<String> values = new ArrayList<String>();
for (Map.Entry<Integer, String> entry : map.entrySet()) {
keys.add(entry.getKey());
values.add(entry.getValue());
}

Intent intent = new Intent(this, Activity2.class);
intent.putExtra("KEYS", keys);
intent.putExtra("VALUES", values);

startActivity(intent);
}
}

Activity2:

public class Activity2 extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Intent intent = getIntent();
List<Integer> keys = (List<Integer>) intent.getSerializableExtra("KEYS");
List<String> values = (List<String>) intent.getSerializableExtra("VALUES");

Map<Integer, String> map = new LinkedHashMap<Integer, String>();
for (int i = 0, n = keys.size(); i < n; i++)
map.put(keys.get(i), values.get(i));

Log.i("Activity2", "" + map);
}
}

This is a bit of a hack, and it's extremely verbose, but it works. I would be interested to know if there is a better solution to this.

TreeMap in Intent becomes HashMap

As mentioned by pbabcdefp in his comment above, the cause of the problem is described here. A more detailed explanation can be found in this blog post.

An easy to implement solution to it is using Gson.

Android How to Pass LinkedHashMap Between Activities?

Try GSON for change :)

Download gson.jar from this link

And add gson-2.2.2.jar file in your project.
Now pass your LinkedHashMap to another activity using GSON

like this(modified this below code as per your need):

MainActivity:::

public class MainActivity extends Activity {
ObjectClass obj=new ObjectClass();
LinkedHashMap<String, ObjectClass> mLinkedHashMap = new LinkedHashMap<String, ObjectClass>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

obj.id=1;
obj.name="hello";

mLinkedHashMap.put("test", obj);

Gson gson = new Gson();
String list = gson.toJson(mLinkedHashMap);
Intent intent = new Intent(this, secondActivity.class);
intent.putExtra("list", list);
startActivity(intent);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}

secondActivity:::

public class secondActivity extends Activity {
LinkedHashMap<String, ObjectClass> mLinkedHashMap = new LinkedHashMap<String, ObjectClass>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String str= getIntent().getStringExtra("list");
Gson gson = new Gson();

Type entityType = new TypeToken< LinkedHashMap<String, ObjectClass>>(){}.getType();
mLinkedHashMap = gson.fromJson(str, entityType);
ObjectClass obj = mLinkedHashMap.get("test");

Log.i("list", ""+obj.id);
}
}

Worked for me. Hope this will help.

And Here is my object class for reference.

public class ObjectClass {

public int id;
public String name;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

How to add gson-2.2.2.jar file.

1) copy your gson-2.2.2.jar from downloaded folder "google-gson-2.2.2" .

2) paste it to your project's asset folder.

3) now go to your project buildpath by right clicking on the your project>Build Path>Configure Build Path..

4) It will open one dialog select java build path from right menu and go to library tag then
Click on "Add Jars..." button as you can see in below image
it will open another dialog to add jar. here select the gson-2.2.2.jar that we added in project's asset folder(step 2).
It will add jar to your project(I already added it in my project as you can see in below image)


Sample Image
5)Now select Order and Export tag and select your gson-2.2.2.jar(see below image).

Sample Image
6)Press OK and now you can use GSON in your project

How to pass Properties object from an intent to another?

Try this:

Properties properties = new Properties();
prop.setProperty("Hello", "Hi!");
/*...*/
Bundle bundle = new Bundle();
bundle.putExtra("prop", properties);

Then in your activity you can get it back, but you must cast it as a HashMap:

Intent intent = this.getIntent();
Bundle bundle = intent.getExtras();
HashMap<String,String> map= (HashMap<String, String>)bundle.getSerializable("prop");
map.get("Hello")

The reason why this works it's completely explained in this solution and as a summary it's because Properties implements Map, and any class that implements Map that you put into a Bundle will come out as a HashMap.

If you still need an object of type Property you can do this:

Properties properties = new Properties();
properties.putAll(map);


Related Topics



Leave a reply



Submit