I have been searching for a way to pass an object from one Activity to another.
Different tutorials stated that the best way to do it is to make the class Parcelable. I've managed to implement it, but I have one question left.
There is a reference to another parcelable object (location) inside the Office class. This tutorial tells to serialize it using dest.writeParcelable(location, flags); and in.readParcelable(LatLng.class.getClassLoader());, but the parcelabler created the code with dest.writeValue(location); and then (LatLng) in.readValue(LatLng.class.getClassLoader());.
I have checked and it worked both ways.
Could somebody please explain what is the difference between these two approaches? Is any of them better for some reasons? Thank you!
public class Office implements Parcelable {
#SuppressWarnings("unused")
public static final Parcelable.Creator<Office> CREATOR = new Parcelable.Creator<Office>() {
#Override
public Office createFromParcel(Parcel in) {
return new Office(in);
}
#Override
public Office[] newArray(int size) {
return new Office[size];
}
};
public final String name;
public final String address;
public final LatLng location;
public Office(String name, String address, LatLng location) {
this.name = name;
this.address = address;
this.location = location;
}
protected Office(Parcel in) {
name = in.readString();
address = in.readString();
// location = (LatLng) in.readValue(LatLng.class.getClassLoader());
location = in.readParcelable(LatLng.class.getClassLoader());
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(address);
// dest.writeValue(location);
dest.writeParcelable(location, flags);
}
}
writeValue is more generic, and since it takes an Object as parameter, internally they check the instanceOf the object to call the specific method. If you know the type, I would stick with using the specific one
Related
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.
I have class tariff and i need to parcelable it.
public class Tariff implements Parcelable{
private String operator;
private Discounts discount;
private boolean unlimited;
private Billings billing;
private String name;
private double price;
private double calculated;
private Call call;
private Sms sms;
I found some advices here but im not sure i´m getting them right.
1) For parcelable enum I found this. Will that parcelable all values of my enum? Or how should i do that?
try {
type = Discounts.valueOf(in.readString());
} catch (IllegalArgumentException x) {
type = null;
}
2) For parcelable another object(for example call) i found this: If the CategoryDate class is one of yours, you can make it Parcelable as well. Then in your class' writeToParcel() call you can call this.date.writeToParcel() and pass it the same Parcel object. This will cause the CategoryDate class to write its data into the same Parcel object which is being used by CloseItPending.
But i´m not sure i got it right. How exactly should i do that?
Parcelable it's really a lot of boilerplate that, coding manually, have a great chance to fall in error.
Use this site: http://www.parcelabler.com/
This tool automatic generate the parcelable fields for your class. Remember to make the Classes declared as class variables to be parcelable too.
The result will be somenthing like this:
public class Tariff implements Parcelable {
private String operator;
private Discounts discount;
private boolean unlimited;
private Billings billing;
private String name;
private double price;
private double calculated;
private Call call;
private Sms sms;
protected Tariff(Parcel in) {
operator = in.readString();
discount = (Discounts) in.readValue(Discounts.class.getClassLoader());
unlimited = in.readByte() != 0x00;
billing = (Billings) in.readValue(Billings.class.getClassLoader());
name = in.readString();
price = in.readDouble();
calculated = in.readDouble();
call = (Call) in.readValue(Call.class.getClassLoader());
sms = (Sms) in.readValue(Sms.class.getClassLoader());
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(operator);
dest.writeValue(discount);
dest.writeByte((byte) (unlimited ? 0x01 : 0x00));
dest.writeValue(billing);
dest.writeString(name);
dest.writeDouble(price);
dest.writeDouble(calculated);
dest.writeValue(call);
dest.writeValue(sms);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<Tariff> CREATOR = new Parcelable.Creator<Tariff>() {
#Override
public Tariff createFromParcel(Parcel in) {
return new Tariff(in);
}
#Override
public Tariff[] newArray(int size) {
return new Tariff[size];
}
};
}
I am getting a bit frustrated with an issue that I cannot seem to fully understand.
I have a listview with items and when I click them I want to pass an object (Parcelable) to a new activity. This is the code below:
lv_Entries.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent getItchesScreen = new Intent(Home.this, Itches.class);
getItchesScreen.putExtra("i", 3);
Entry e = entries.get(position);
getItchesScreen.putExtra("entry", e);
startActivity(getItchesScreen);
}
});
Now, I have the "i" extra there for debugging purposes. I was just sending "entry" and when I got the intent on the activity it didn't work. Code below:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_itches);
tv_date = (TextView) findViewById(R.id.tv_date);
Bundle b = getIntent().getExtras();
entry = b.getParcelable("entry");
tv_date.setText(entry.getDate());
itches = entry.getItches();
itchesAdapter = new ItchAdapter(this, itches);
ListView lv_Itches = (ListView) findViewById(R.id.lv_itches);
lv_Itches.setAdapter(itchesAdapter);
}
So when I read my bundle there is nothing at all. No "entry" key and no "i" key (I debugged to read i using watch feature)
BUT! If I don't send "entry" and only send "i" and I debug to catch "i" I do get it!
I have no idea why sending entry is ruining things but I cannot find any answer. I debugged the object and it does find it though .get(position).
Hope anyone can give me any ideas, and sorry for any trouble.
EDIT
Below is the code for Entry:
public class Entry implements Parcelable{
private String date;
private ArrayList<Itch> itches;
public Entry(String date){
this.date = date;
itches = new ArrayList<Itch>();
}
// PARCELABLE
public Entry(Parcel source){
date = source.readString();
source.readTypedList(itches, Itch.CREATOR);
}
public void AddItch(Itch itch){
itches.add(itch);
}
// get intensity average for the itches
public int IntensityAverage(){
int intensity = 0;
for(Itch i : itches){
intensity += i.getIntensity();
}
return intensity/itches.size();
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public ArrayList<Itch> getItches() {
return itches;
}
public void setItches(ArrayList<Itch> itches) {
this.itches = itches;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(date);
dest.writeTypedList(itches);
}
public static final Parcelable.Creator<Entry> CREATOR =
new Parcelable.Creator<Entry>() {
public Entry createFromParcel(Parcel source) {
return new Entry(source);
}
public Entry[] newArray(int size) {
return new Entry[size];
}
};
}
Itch class is also Parceable. I am populating correctly (no crashes on Android at least) the ListView with it.
For convenience I place the code here aswell:
public class Itch implements Parcelable{
private String time;
private String local;
private int intensity;
public Itch(String time, String local, int intensity){
this.time = time;
this.local = local;
this.intensity = intensity;
}
// PARCELABLE
public Itch(Parcel source){
time = source.readString();
local = source.readString();
intensity = source.readInt();
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getLocal() {
return local;
}
public void setLocal(String local) {
this.local = local;
}
public int getIntensity() {
return intensity;
}
public void setIntensity(int intensity) {
this.intensity = intensity;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(time);
dest.writeString(local);
dest.writeInt(intensity);
}
public static final Parcelable.Creator<Itch> CREATOR =
new Parcelable.Creator<Itch>() {
public Itch createFromParcel(Parcel source) {
return new Itch(source);
}
public Itch[] newArray(int size) {
return new Itch[size];
}
};
}
Alright so... What was the problem? Simple.
The reason why the parcelable always came out null was because a stupid error was occurring. Which error?
Well, okay so take a look at this piece of code:
entry = b.getParcelable("entry");
What is it saying? It is saying that entry will be equal to the parcelable "entry" key. But what does that really mean? Look at entry constructor.
// PARCELABLE
public Entry(Parcel source){
date = source.readString();
source.readTypedList(itches, Itch.CREATOR);
}
So when you say that entry is equals to a parcelable, then you will call this constructor in the Entry class that I have posted. But why is it wrong you might ask?
Well, so take a look. We're giving ArrayList itches to the method readTypeList. but... wait a second. If that is a constructor that means that we're building from 0... So... is itches initiated? No it is not! Because I was only initiating itches in the "normal" constructor!
public Entry(String date){
this.date = date;
itches = new ArrayList<Itch>();
}
So the solution is...
// PARCELABLE
public Entry(Parcel source){
date = source.readString();
//add this if condition!
if (itches == null) {
itches = new ArrayList<Itch>();
}
source.readTypedList(itches, Itch.CREATOR);
}
And thats it. That fixes our problem! :)
If other error occurs please be aware:
Make SURE that your key is correct. So check out for any typos in your getting extras.
entry = b.getParcelable("entyr");
as instead of
entry = b.getParcelable("entry");
And any other type of error like that.
That is not a good practive, you should have a variable that has the "entry" written on it so you never have this type of error mistakes. I have it in my code because I am fast-programming to build up a prototype :)
Happy coding!
have you tried doing this in onCreate()
Intent i = getIntent();
if(i.hasExtra("entry")){
entry = i.getParcelableExtra("entry");
}else{
Log.v("EXTRAS", "entry not found");
}
I'm creating a location based reminder application.
I've got a custom object called Reminder, which stores latitude, longitude, the location name and the subject of the reminder - Aswell as what profile it belongs to.
I'm then inputting the location name and sujbect into a ListView (Using cardlibs here too).
However now I need to save the data for when I close the application, and was wondering the best way to go about this - Do I store the entire 'Reminder' object or do I just store multiple pieces of data in Shared Preferences / A database.
This is my code - It's very inefficient/Ugly:
Reminder:
public class Reminder implements Parcelable {
public double latitude;
public double longitude;
public String subject;
public String locationName;
public String profile;
public Reminder() {
}
public Reminder(Parcel in) {
String[] data = new String[5];
in.readStringArray(data);
this.subject = data[0];
this.locationName = data[1];
this.latitude = Double.parseDouble(data[2]);
this.longitude = Double.parseDouble(data[3]);
this.profile = data[4];
}
public String getProfile() {
return profile;
}
public double getLatitude() {
return latitude;
}
public String getLocationName() {
return locationName;
}
public double getLongitude() {
return longitude;
}
public String getSubject() {
return subject;
}
public void setProfile(String profile) {
this.profile = profile;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public void setLocationName(String locationName) {
this.locationName = locationName;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public void setSubject(String subject) {
this.subject = subject;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(new String[] { this.subject, this.locationName,
String.valueOf(this.latitude), String.valueOf(this.longitude), this.profile });
}
public static final Parcelable.Creator<Reminder> CREATOR = new Parcelable.Creator<Reminder>() {
public Reminder createFromParcel(Parcel source) {
// TODO Auto-generated method stub
return new Reminder(source); // using parcelable constructor
}
public Reminder[] newArray(int size) {
// TODO Auto-generated method stub
return new Reminder[size];
}
};
}
List using a fragment:
public class HomeFragment extends Fragment {
public static ArrayList<Card> cards = new ArrayList<Card>();
Reminder reminder;
public HomeFragment() {
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
reminder = getActivity().getIntent().getParcelableExtra("reminder");
Card card = new Card(getActivity());
CardHeader cardHeader = new CardHeader(getActivity());
if (reminder != null) {
cardHeader.setTitle(reminder.getSubject());
card.addCardHeader(cardHeader);
card.setTitle(reminder.getLocationName());
cards.add(card);
}
CardArrayAdapter mCardArrayAdapter = new CardArrayAdapter(
getActivity(), cards);
CardListView listView = (CardListView) getActivity().findViewById(
R.id.card_list);
mCardArrayAdapter.notifyDataSetChanged();
if (listView != null)
listView.setAdapter(mCardArrayAdapter);
}
Any criticism on how my code could of been wrote better would also be really beneficial, as I feel I'm being a huge rookie and missing something obvious which would make it more efficient.
Thanks!
Basically the choice to use shared preferences vs a database hinges on a number of things:
1) How many objects are we talking about? How complex are they?
2) Do you intend on searching/manipulating these objects or are you just saving/loading them.
3) Do you intend to export/move/send objects?
4) Do you want the objects to be recoverable across application contexts - that is beyond only the single app in question.
In your specific case I would use a database.
http://developer.android.com/guide/topics/data/data-storage.html#pref
As you can see in the docs here prefs isn't really intended for Object storage - it's for key value pairs. While you could store an object as a number of key value pairs in the shared prefs and reconstruct from there, you would have to go through some weird model conversions to do this for multiple objects. In short it doesn't really make sense.
i have an objects in an custom arraylist as "finaljsoncontent", and now i am trying to pass this "finaljsoncontent" array to another Activity, and i have also tried getters and setters, and also bundle, but i cant, help me how to do this.
Thanks in advance.
Check this out: How do I pass an object from one activity to another on Android?
Your class "JSonKey" should implement parcealable or serializable so that Android can "send" it from an activity to the other activity.
You could try implementing Parcelable, then you can pass it in a bundle. You will need to reduce your object to mostly primitive types to do this. Otherwise you can extend the Application class and store it there. You would retrieve that using the call to getApplicationContext(). Or, of course, you could always create some sort of static globals class that all of your classes can reference.
Here is one of my implementations of parcelable..
package warrior.mail.namespace;
import android.os.Parcel;
import android.os.Parcelable;
public class JView implements Parcelable {
public String subject;
public String from;
public boolean unread;
public String body;
public int inboxIndex;
private long id;
public static final Parcelable.Creator<JView> CREATOR = new Parcelable.Creator<JView>() {
public JView createFromParcel(Parcel in) {
return new JView(in);
}
public JView[] newArray(int size) {
return new JView[size];
}
};
public JView(){
body = "";
}
public JView(String subject,String from,boolean unread){
body = "";
this.subject = subject;
this.from = from;
this.unread = unread;
}
public JView(Parcel parcel){
subject = parcel.readString();
from = parcel.readString();
body = parcel.readString();
unread = parcel.createBooleanArray()[0];
inboxIndex = parcel.readInt();
}
#Override
public int describeContents() {
return inboxIndex;
}
#Override
public void writeToParcel(Parcel out, int arg1) {
out.writeString(subject);
out.writeString(from);
out.writeString(body);
boolean[] array = new boolean[] {unread};
out.writeBooleanArray(array);
out.writeInt(inboxIndex);
}
public void setIndex(int index){
inboxIndex = index;
}
public void setUnread(boolean arg){
unread = arg;
}
public void setContent(String content){
body = content;
}
public void setSubject(String subject){
this.subject = subject;
}
public void setFrom(String f){
from = f;
}
public void setId(long arg){
id = arg;
}
public long getId(){
return id;
}
public void updateIndex(){
}
}
You can either make your class Parcelable(android specific) or make it serializable like in java(just write implements Serializable with your class)