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 List
s, 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)
5)Now select Order and Export tag and select your gson-2.2.2.jar(see below 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
Java.Lang.Outofmemoryerror: Bitmap Size Exceeds Vm Budget
Only Load Layout When Firebase Calls Are Complete
Add a Background Image to Shape in Xml Android
How to Simulate Touch from Background Service with Sendevent or Other Way
Programmatically Getting the Gateway and Subnet Mask Details
Fragmentcontainerview Using Findnavcontroller
Why Is My Smallicon for Notifications Always Greyed Out
How to Restrict the Edittext to Accept Only Alphanumeric Characters
Programmatic Views How to Set Unique Id'S
Set the Layout Weight of a Textview Programmatically
Stopping & Starting Music on Incoming Calls
How to Use Setarguments() and Getarguments() Methods in Fragments
Ndk Resolution Outcome: Project Settings: Gradle Model Version=5.4.1, Ndk Version Is Unknown Error
Android Pendingintent Extras, Not Received by Broadcastreceiver
Android Facebook 4.0 Sdk How to Get Email, Date of Birth and Gender of User