I have a Linked List in one activity (A) that I want to share with another Activity (B).
The list contains a username of type string and contains coordinates of type LatLng. I am also using Intent and bundle to share data between activities. I tried using Parcelable but unable to figure out how to use it. Here is the code I have:
data.java
public class data implements Parcelable{
private LatLng coordinates;
private String name;
public data() {
name = null;
coordinates = null;
}
public data(String name, LatLng coordinates)
{
this.name = name;
this.coordinates = coordinates;
}
public data(Parcel in) {
coordinates = in.readParcelable(LatLng.class.getClassLoader());
name = in.readString();
}
public static final Creator<data> CREATOR = new Creator<data>() {
#Override
public data createFromParcel(Parcel in) {
return new data(in);
}
#Override
public data[] newArray(int size) {
return new data[size];
}
};
public LatLng getLatLng () {
return coordinates;
}
#Override
public int describeContents() {
return hashCode();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeParcelable(coordinates, flags);
}
}
Activity A
public class A extends FragmentActivity implements
OnMapReadyCallback,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
GoogleMap.OnMyLocationButtonClickListener,
ActivityCompat.OnRequestPermissionsResultCallback {
Button switchToSeek;
double mLatitude;
double mLongitude;
LinkedList<data> storedData = new LinkedList<>();
protected void onCreate(Bundle savedInstanceState) {
...
switchToSeek.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getCurrentLocation();
Intent intent = new Intent(A.this, B.class);
Bundle xy = new Bundle();
xy.putDouble("x", mLatitude);
xy.putDouble("y", mLongitude);
xy.putParcelable("list", storedData); <---------- error: wrong second arugment
intent.putExtra("xy", xy);
A.this.startActivity(intent);
}
});
Activity B
public class B extends FragmentActivity implements OnMapReadyCallback {
double mLatitude;
double mLongitude;
LatLng current;
GoogleMap gMap;
LinkedList <data> copyData = new LinkedList<>();
#Override
public void onMapReady(GoogleMap googleMap) {
gMap = googleMap;
...
Intent intent = getIntent();
Bundle xy = intent.getBundleExtra("xy");
if (xy != null) {
mLatitude = xy.getDouble("x");
mLongitude = xy.getDouble("y");
}
/***** Call linked list here and set equal to copyData *****/
current = new LatLng(mLatitude, mLongitude);
gMap.moveCamera(CameraUpdateFactory.newLatLngZoom(current, 18.0f));
}
There is no easy way to do that, since LinkedList does not implement serializable or parcelable.
You CAN implement your own linked list class and make it a serializable/parcelable object which can then be passed.
Or you can convert its content into another data type such as an array and then recreate the linkedlist.* THIS IS HIGHLY INEFFICIENT
I believe there are other ways but this is a standard problem in android dev. Maybe try using fragments if possible and passing the linkedlist through a setter()
If the list is not huge, you can do it using the following helper class:
public class ParcelableLinkedList<E extends Parcelable> implements Parcelable {
private final LinkedList<E> linkedList;
public final Creator<ParcelableLinkedList> CREATOR = new Creator<ParcelableLinkedList>() {
#Override
public ParcelableLinkedList createFromParcel(Parcel in) {
return new ParcelableLinkedList(in);
}
#Override
public ParcelableLinkedList[] newArray(int size) {
return new ParcelableLinkedList[size];
}
};
public ParcelableLinkedList(Parcel in) {
// Read size of list
int size = in.readInt();
// Read the list
linkedList = new LinkedList<E>();
for (int i = 0; i < size; i++) {
linkedList.add((E)in.readParcelable(ParcelableLinkedList.class.getClassLoader()));
}
}
public ParcelableLinkedList(LinkedList<E> linkedList) {
this.linkedList = linkedList;
}
LinkedList<E> getLinkedList() {
return linkedList;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
// Write size of the list
parcel.writeInt(linkedList.size());
// Write the list
for (E entry : linkedList) {
parcel.writeParcelable(entry, flags);
}
}
}
In your onClick() method, add the data to the Bundle like this:
xy.putParcelable("list", new ParcelableLinkedList<data>(storedData));
To extract the data from the Bundle, do this:
copyData = ((ParcelableLinkedList<data>)xy.getParcelable("list")).getLinkedList();
I haven't actually compiled and tested this code, but it should work.
If the list is really huge, you are better off storing it in a static member variable in one class and then just referencing it from the other. This isn't normally the way you want to do things in Android, but it is sometimes more expedient to do this than to serialize and deserialize a huge amount of data just to pass it between 2 activities that have access to the same memory space.
Related
Currently using an Intent with startActivity() to switch between 2 activities which share an abstract superclass. However, whenever startActivity() is called the custom object inherited from the abstract superclass gets reset. Is there anyway to maintain this object between startActivity() calls? Serializing the object with OnSavedInstanceState does not work because this object contains a LinkedList.
Every time a class is created regardless if it extends a superclass (in this case an Activity), the superclass is recreated. You would extend the Activity to share common methods/functions and imports....
You want to stay away from Serializable, so you want your object class to implement Parcelable:
public class CustomObject implements Parcelable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
}
public CustomObject() {
}
protected CustomObject(Parcel in) {
this.name = in.readString();
}
public static final Parcelable.Creator<CustomObject> CREATOR = new Parcelable.Creator<CustomObject>() {
#Override
public CustomObject createFromParcel(Parcel source) {
return new CustomObject(source);
}
#Override
public CustomObject[] newArray(int size) {
return new CustomObject[size];
}
};
}
In the first activity you want to pass the List to the second Activity using an Intent:
public class StartActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(StartActivity.this, ReceiveDataActivity.class);
i.putParcelableArrayListExtra("KEY", getCustomObjectList());
startActivity(i);
}
});
}
private ArrayList<CustomObject> getCustomObjectList() {
ArrayList<CustomObject> itemList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
CustomObject customObject = new CustomObject();
customObject.setName("Name " + i);
itemList.add(customObject);
}
return itemList;
}
}
Then to get the list you would use getIntent():
public class ReceiveDataActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_receive_data);
List<CustomObject> itemList = getIntent().getParcelableArrayListExtra("KEY");
Toast.makeText(this, "List size = " + itemList.size(), Toast.LENGTH_SHORT).show();
}
}
e.g
Class City that needs to implement Parcelable has field of type Location which implements Serializable. class Location is imported from a third party jar file and I cannot modify it. How do I successfully implement Parcelable for class City with the Location field ?
Simply use Parcel.writeSerializable() and Parcel.readSerializable()
public class MyParcelableObject implements Parcelable {
public static final Parcelable.Creator<MyParcelableObject> CREATOR =
new Parcelable.Creator<MyParcelableObject>() {
#Override
public MyParcelableObject createFromParcel(Parcel in) {
return new MyParcelableObject(in);
}
#Override
public MyParcelableObject[] newArray(int size) {
return new MyParcelableObject[size];
}
};
private final MySerializableObject mySerializableField;
private MyParcelableObject(Parcel in) {
this.mySerializableField = (MySerializableObject) in.readSerializable();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeSerializable(mySerializableField);
}
#Override
public int describeContents() {
return 0;
}
}
I'm devlopping an Android app made of multiple Activities and I have to pass ab Object between them, but I can't pass it by using intents because the class of the object doesn't implement serializable, how can I do it?
I CAN'T MODIFY THE SOURCE CODE OF MY CLASS
Thanks :)
public class MyClass { //stuff }
//I can't modify this source code
MyClass m = new MyClass(); //object I have to pass
Suppose there is a data object class named StudentDataObject having some data types.
StudentDataObject studentDataObject = new StudentDataObject();
Gson gson = new Gson();
String studentDataObjectAsAString = gson.toJson(studentDataObject);
Now we are passing it from one activity to another activity using intent.
Intent intent = new Intent(FromActivity.this, ToActivity.class);
intent.putExtra("MyStudentObjectAsString", studentDataObjectAsAString);
startActivity(intent);
Now we are in new activity, we get that object here using following line.
Gson gson = new Gson();
String studentDataObjectAsAString = getIntent().getStringExtra("MyStudentObjectAsString");
StudentDataObject studentDataObject = gson.fromJson(studentDataObjectAsAString, StudentDataObject.class);
Activity itself know where from I am called, so we can directly write getIntent() method.
Here we only need to add one dependency of GSON we can add it using following line in build.gradle file.
compile 'com.google.code.gson:gson:2.6.2'
And one thing is that implement StudentDataObject as a Parcelable and if showing error then just press alt+Enter and implement methods.
Try this once, Hope it will work.
Sample Example for StudentDataObject should be like :-
public class StudentDataObject implements Parcelable {
// fields
//empty constructor
//parameterised constructor
//getters and setters
//toString method
//last implement some Parcelable methods
}
First of all create Parcelable data model.
public class DataModel 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<DataModel> CREATOR
= new Parcelable.Creator<DataModel>() {
public DataModel createFromParcel(Parcel in) {
return new DataModel(in);
}
public DataModel[] newArray(int size) {
return new DataModel[size];
}
};
private DataModel(Parcel in) {
mData = in.readInt();
}
}
put object into intent
intent.putExtra("KEY", object);
get object from intent
object = getIntent().getExtras().getParcelable("KEY");
This code may help you:
public class EN implements Serializable {
//... you don't need implement any methods when you implements Serializable
}
FirstActivity
EN enumb = new EN();
Intent intent = new Intent(getActivity(), NewActivity.class);
intent.putExtra("en", enumb); //second param is Serializable
startActivity(intent);
SecandActivity
Bundle extras = getIntent().getExtras();
if (extras != null) {
en = (EN)getIntent().getSerializableExtra("en"); //Obtaining data
}
Passing data through intent using Serializable
Here is my object class Book.java
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
// book basics
private String title;
private String author;
// main constructor
public Book(String title, String author) {
this.title = title;
this.author = author;
}
// getters
public String getTitle() { return title; }
public String getAuthor() { return author; }
// write object values to parcel for storage
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(title);
dest.writeString(author);
}
public Book(Parcel parcel) {
title = parcel.readString();
author = parcel.readString();
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
#Override
public Book createFromParcel(Parcel parcel) {
return new Book(parcel);
}
#Override
public Book[] newArray(int size) {
return new Book[0];
}
};
public int describeContents() {
return hashCode();
}
}
Now you can pass object like this
Button button = (Button) findViewById(R.id.submit_button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Book book = new Book(mBkTitle.getText().toString(),
mBkAuthor.getText().toString());
Intent intent = new Intent(MainActivity.this, BookActivity.class);
intent.putExtra("Book", book);
startActivity(intent);
}
});
Now object will be received like this in receiving ReceivingActivity.java
Intent intent = getIntent();
Book book = intent.getParcelableExtra("Book");
mBkTitle.setText("Title:" + book.getTitle());
mBkAuthor.setText("Author:" + book.getAuthor());
You need to implement parcelable and then pass it via intent. Dont use Serializable cause is way slower than parcelable.
Read here how to make your object parcelable: https://developer.android.com/reference/android/os/Parcelable.html
after you dont it, pass your object like this:
intent.putExtra("KEY", your_object);
to read it:
getIntent().getExtras().getParcelable("KEY");
Extend the class and implement serializable or parcelable in the inherited class and use its objects as in other answers.
Class NewClass extends MyClass implements serializable {
//Create a constructor matching super
}
Use objects of this class instead of my class
You can pass a custom object from one activity to another through intent in 2 ways.
By implements Serializable
By implements Parcelable
(1) By implements Serializable no need to do anything just implement Serializable
into your class like
public class Note implements Serializable {
private int id;
private String title;
}
(2) By implementing Parcelable (you have to follow the Parcel write and read with same order)
public class Note implements Parcelable {
private int id;
private String title;
public Note() {
}
Note(Parcel in){
this.id = in.readInt();
this.title = in.readString();
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(title);
}
public static final Parcelable.Creator<Note> CREATOR = new Parcelable.Creator<Note>(){
#Override
public Note createFromParcel(Parcel source) {
return new Note(source);
}
#Override
public Note[] newArray(int size) {
return new Note[size];
}
};
}
and then in your activity
Activity A
intent.putExtra("NOTE", note);
Activity B
Note note = (Note) getIntent().getExtras().get("NOTE");
Imp: Parcelable is 10 times faster than Serializable
I have a class that I'm using for parsing data from a csv file (called CSVReader), inside of that there is some methods for the parsing as well as a couple of strings and a list that is of a custom object type (List allRecords).
What I'm doing is when the app loads, parsing all the data into that list and then trying to pass that information along to the next activity but inside the next activity I keep getting allRecords as being null.
LoginActivity
CSVReader data = new CSVReader();
data.populateRecords(this);
Intent intent = new Intent(LoginActivity.this, Find.class);
Bundle bundle = new Bundle();
bundle.putParcelable("list", data);
intent.putExtra("bundle", bundle);
startActivity(intent);
I've gone through the debugger and the bundle definitely has the data in there.
Find
Intent intent = getIntent();
Bundle bundle = intent.getBundleExtra("bundle");
data = (CSVReader) bundle.getParcelable("list");
Using the debugger still, and mMap (in the bundle) is now null and so is data.
Am I doing something wrong? Both classes DummyData and CSVReader implement Parcelable.
EDIT: Adding custom class CSVReader:
List<DummyData> allRecords;
private String base;
private String location;
private String partner;
public static Creator<CSVReader> CREATOR = new Creator<CSVReader>(){
#Override
public CSVReader createFromParcel(Parcel source){
return new CSVReader(source);
}
#Override
public CSVReader[] newArray(int size) {
return new CSVReader[size];
}
};
private CSVReader(Parcel in){
allRecords = new ArrayList<DummyData>();
in.readTypedList(allRecords, DummyData.CREATOR);
base = in.readString();
location = in.readString();
partner = in.readString();
}
...
...
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(base);
dest.writeString(location);
dest.writeString(partner);
dest.writeList(allRecords);
}
[EDIT]
I was looking over your code again, and noticed you're writing the List to the parcel LAST and reading the list from the parcel FIRST. You have to read items from the parcel in the order they are written. Try putting dest.writeList(allRecords) at the top of the method so it is the first item written, or you can put in.readList(allRecords, DummyData.class.getClassLoader()) at the bottom of the list in that method.
Give it a shot.
//////////////////////////////////////////////
From the docs,
public final void readTypedList (List<T> list, Creator<T> c)
Read into the given List items containing a particular object type
that were written with writeTypedList(List) at the current
dataPosition(). The list must have previously been written via
writeTypedList(List) with the same object type.
You're using writeList() to write the data to the parcel. Try using writeTypedList(). You could also try changing readTypedList() with readList() I believe. Something like:
in.readList(allRecords, CSVReader.class.getClassLoader());
Hope this helps.
If you plan to pass data to another activity, you need to use objects of classes that implement Parcelable. Here is an example,
package com.weather.model;
import java.util.ArrayList;
import java.util.List;
import android.os.Parcel;
import android.os.Parcelable;
public class Forcast implements Parcelable {
private String id;
private String code;
private String message;
private City city;
private String count;
private List<Weather> weatherList = new ArrayList<Weather>();
public Forcast(){
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public String getCount() {
return count;
}
public void setCount(String count) {
this.count = count;
}
public List<Weather> getWeatherList() {
return weatherList;
}
public void setWeatherList(List<Weather> weather) {
this.weatherList = weather;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(code);
dest.writeString(message);
dest.writeParcelable(city, flags);
dest.writeString(count);
dest.writeList(weatherList);
}
public static final Parcelable.Creator<Forcast> CREATOR = new Parcelable.Creator<Forcast>() {
#Override
public Forcast createFromParcel(Parcel source) {
return new Forcast(source);
}
#Override
public Forcast[] newArray(int size) {
return new Forcast[size];
}
};
protected Forcast(Parcel in){
id = in.readString();
code = in.readString();
message = in.readString();
city = (City)in.readParcelable(City.class.getClassLoader());
count = in.readString();
in.readList(weatherList, Weather.class.getClassLoader());
}
}
I included City model so you can see how it goes for an object in comparison to list of objects. Assume you have a Forcast instance with all the values as 'forcast' ( Forcast forcast = new Forcast() or something similar)
Intent intent = new Intent(this, SomeActivity.class);
intent.putExtra(com.weather.model,forcast)
startActivity(intent)
I hope that helps
You need to put bundle object by using putExtras method. Then you can get the bundle by using getIntent().getExtras().
Intent intent = new Intent(LoginActivity.this, Find.class);
Bundle bundle = new Bundle();
bundle.putParcelable("list", data);
intent.putExtras( bundle);
startActivity(intent);
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
data = (CSVReader) bundle.getParcelable("list");
I have an ArrayList of type GeoPoint.
private List<GeoPoint> points = new ArrayList<GeoPoint>();
I want to pass points to another Activity and retrive the data in that activity. How do I do it? I know I have to use the parcelable but I searched, but could not find a way to pass ArrayLists.
This function will help you: http://developer.android.com/reference/android/content/Intent.html#putParcelableArrayListExtra(java.lang.String, java.util.ArrayList<? extends android.os.Parcelable>)
But the problem is, that GeoPoint is not Parcelable. Well, you can do a workaround here:
1) Create a class, that implements Parcelable:
public class ParcelableGeoPoint implements Parcelable {
private GeoPoint geoPoint;
public ParcelableGeoPoint(GeoPoint point) {
geoPoint = point;
}
public GeoPoint getGeoPoint() {
return geoPoint;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(geoPoint.getLatitudeE6());
out.writeInt(geoPoint.getLongitudeE6());
}
public static final Parcelable.Creator<ParcelableGeoPoint> CREATOR
= new Parcelable.Creator<ParcelableGeoPoint>() {
public ParcelableGeoPoint createFromParcel(Parcel in) {
return new ParcelableGeoPoint(in);
}
public ParcelableGeoPoint[] newArray(int size) {
return new ParcelableGeoPoint[size];
}
};
private ParcelableGeoPoint(Parcel in) {
int lat = in.readInt();
int lon = in.readInt();
geoPoint = new GeoPoint(lat, lon);
}
}
2) when sending to the other activity (points is your List<GeoPoint>:
ArrayList<ParcelableGeoPoint> pointsExtra = new ArrayList<ParcelableGeoPoint>();
foreach(GeoPoint point: points) {
pointsExtra.add(new ParcelableGeoPoint(point));
}
intent.putExtra("geopoints", pointsExtra);
3) in the called activity:
ArrayList<ParcelableGeoPoint> pointsExtra = getIntent().getParcelableArrayListExtra("geopoints");
ArrayList<GeoPoint> points = new ArrayList<GeoPoint>();
foreach(ParcelableGeoPoint point: pointsExtra) {
points.add(point.getGeoPoint());
}
code should work, but is untested.