I have problems with reaching parcelable between activities. When I inspect the object before sending it to another activity it looks fine, but after receiving it I can only see a strange id and the rest of the fields is nulled. Why does it happen? Maybe the way I do it is wrong? It would be best, you will take a look at the code parts:
This is the parcelable object:
#Data
#NoArgsConstructor
public class Scene implements Parcelable
{
private Long id;
private String name;
private Light light;
private Track effect;
private Track music;
private Track ambience;
protected Scene(Parcel in)
{
if (in.readByte() == 0) { id = null; } else { id = in.readLong(); }
name = in.readString();
}
public static final Creator<Scene> CREATOR = new Creator<Scene>()
{
#Override
public Scene createFromParcel(Parcel in)
{
return new Scene(in);
}
#Override
public Scene[] newArray(int size)
{
return new Scene[size];
}
};
#Override
public int describeContents()
{
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags)
{
Map<String, Track> audio = new HashMap<>();
audio.put(Tags.EFFECT.value(), effect);
audio.put(Tags.MUSIC.value(), music);
audio.put(Tags.AMBIENCE.value(), ambience);
dest.writeLong(id);
dest.writeString(name);
dest.writeMap(audio);
}
}
The scene has a map containing audio files. They are serializable (or should it be better also parcelable?) Nothing complicated, simple POJO:
#Data
public class Track implements Serializable
{
private Long id;
private String name;
private String path;
private Long duration;
private String artist;
private String tag;
}
And finally how I get the object:
public class AudioPlayer extends Service
[...]
public int onStartCommand(Intent intent, int flags, int startId)
{
Scene scene = intent.getParcelableExtra("scene");
[...]
}
}
When I inspect the object by intent.setParcelableExtra(), I get this:
Scene(id=7, name=test, light=null, effect=Track(id=10, name=file.mp3, path=/some/path/file.mp3, duration=13662, artist=null, tag=effect), music=Track(id=11, name=other_file.mp3, path=/some/other/path/other_file.mp3, duration=189492, artist=null, tag=music), ambience=Track(id=12, name=another_other_file.mp3, path=/some/another/other/path/another_other_file.mp3, duration=10109, artist=null, tag=ambience))
When I inspect the object by intent.getParcelableExtra(), I get this:
Bundle[{scene=Scene(id=17179869184, name=null, light=null, effect=null, music=null, ambience=null)}]
I tried also make the Object Track parcelable but the effect was the same.
Please help me solve this issue. I don´t understeand what goes wrong.
When implementing Parcelable, you must guarantee that everything you "write" is exactly matched by a corresponding "read" call. Let's look at your code:
#Override
public void writeToParcel(Parcel dest, int flags)
{
Map<String, Track> audio = new HashMap<>();
audio.put(Tags.EFFECT.value(), effect);
audio.put(Tags.MUSIC.value(), music);
audio.put(Tags.AMBIENCE.value(), ambience);
dest.writeLong(id);
dest.writeString(name);
dest.writeMap(audio);
}
protected Scene(Parcel in)
{
if (in.readByte() == 0) { id = null; } else { id = in.readLong(); }
name = in.readString();
}
Here you are writing a long, then a string, and then a map. But you are reading a byte, then conditionally a long, and then a string. You never read the map back out, and you also never write your light value.
These issues will need to be addressed. Here's what that might look like:
#Override
public void writeToParcel(Parcel dest, int flags)
{
if (id != null) {
dest.writeInt(1);
dest.writeLong(id);
} else {
dest.writeInt(0);
}
dest.writeString(name);
dest.writeSerializable(light);
dest.writeSerializable(effect);
dest.writeSerializable(music);
dest.writeSerializable(ambience);
}
protected Scene(Parcel in)
{
if (in.readInt() == 1) {
this.id = in.readLong();
} else {
this.id = null;
}
this.name = in.readString();
this.light = (Light) in.readSerializable();
this.effect = (Track) in.readSerializable();
this.music = (Track) in.readSerializable();
this.ambience = (Track) in.readSerializable();
}
Related
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
Please help me how to solve this issue ?
Parcelable interface i was implemented in customer pojo object. please help me how to read customer object in activity 2 ?
Customer.java
public Customer implements Parcelable{
private String name;
private String phone;
private List<AccountDetails> accoutDetails;
/getter and setters
public int describeContents() {
return 0;
}
public Customer(Parcel in) {
name= in.readString();
phone= in.readString();
accoutDetails= new ArrayList<AccountDetails>();
in.readList(accoutDetails,null);
}
public static final Parcelable.Creator<Customer> CREATOR = new Parcelable.Creator<Customer>() {
public Customer createFromParcel(Parcel in) {
return new Customer(in);
}
public Customer[] newArray(int size) {
return new Customer[size];
}
};
#Override
public void writeToParcel(Parcel dest, int arg1) {
// TODO Auto-generated method stub
dest.writeString(this. name);
dest.writeString(this.phone);
dest.writeList(accoutDetails);
}
}
In activity 1 used below code:
Customer selected_row=(Customer) parent.getAdapter().getItem(position);
Intent intent = new Intent(getApplicationContext(), Activity2.class);
Bundle bundle = new Bundle();
bundle.putParcelable("selected_customer", selected_row);
intent.putExtras(bundle);
startActivity(intent);
Activity 2:
Customer cust_object = getBundle.getParcelable("selected_customer");
Please find the below exception:
java.lang.RuntimeException: Parcel android.os.Parcel#1219dd0f: Unmarshalling unknown type code 6881396 at offset 660
at android.os.Parcel.readValue(Parcel.java:2228)
at android.os.Parcel.readListInternal(Parcel.java:2526)
at android.os.Parcel.readList(Parcel.java:1661)
Please help me how to read customer object in activity 2 ?
Serialization, although supported, is not recommended for Android. See for example this post.
I think that your issue is on this line of code:
in.readList(accoutDetails,null);
Since accountDetails is a List of custom AccountDetails objects you need AccountDetails to also implement Parcelable.
Do that, and then change the above line of code to:
in.readTypedList(accoutDetails, AccountDetails.CREATOR);
EDIT 1
Also, change:
public Customer(Parcel in) {
name= in.readString();
phone= in.readString();
accoutDetails= new ArrayList<AccountDetails>();
in.readTypedList(accoutDetails,null);
}
to:
public Customer(Parcel in) {
name= in.readString();
phone= in.readString();
in.readTypedList(accoutDetails, AccountDetails.CREATOR);
}
And change:
#Override
public void writeToParcel(Parcel dest, int arg1) {
// TODO Auto-generated method stub
dest.writeString(this. name);
dest.writeString(this.phone);
dest.writeList(accoutDetails);
}
To:
#Override
public void writeToParcel(Parcel dest, int arg1) {
dest.writeString(this.name);
dest.writeString(this.phone);
dest.writeTypedList(accoutDetails);
}
EDIT 2
public Customer implements Parcelable{
private String name;
private String phone;
private List<AccountDetails> accoutDetails = new ArrayList<AccountDetails>();
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 trying to pass a Parceble Extra to another activity using this example, but when I try get it on my second activity NullPointerExeception shows up, could somebody help me?
My Parcelable class:
public class MetaDados implements Parcelable {
private int codigoInstituicao;
// . . .
public MetaDados(int codigoInstituicao, int ano, String offlineUuid, String sigla, String nameInst,
String startedDate, String name, String finishedDate, long size) {
this.codigoInstituicao = codigoInstituicao;
// . . .
}
public int getCodigoInstituicao() {
return codigoInstituicao;
}
public void setCodigoInstituicao(int codigoInstituicao) {
this.codigoInstituicao = codigoInstituicao;
}
//getters and setters . . .
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(codigoInstituicao);
// . . .
}
public static final Parcelable.Creator<MetaDados> CREATOR = new Parcelable.Creator<MetaDados>() {
public MetaDados createFromParcel(Parcel in) {
return new MetaDados(in);
}
public MetaDados[] newArray(int size) {
return new MetaDados[size];
}
};
private MetaDados(Parcel in) {
codigoInstituicao = in.readInt();
//. . .
}
}
My AsynkTask how start my other activity:
ArrayList<MetaDados> metaDadosFull = new ArrayList<MetaDados>();
ArrayList<MetaDados> metaDadosPres = new ArrayList<MetaDados>();
Intent it = new Intent(activity, DownloadSelectionActivity.class);
it.putExtra("metaDadosFull", metaDadosFull);
it.putExtra("metaDadosPres", metaDadosPres);
activity.startActivity(it);
And my DownloadSelectionActivity where I try to get it:
ArrayList<MetaDados> fullList = (ArrayList<MetaDados>) getIntent().getParcelableExtra("metaDadosFull");
for (MetaDados metaDados : fullList) {
Log.d(Constants.DOWNLOAD_SELECTED_ACTIVITY, metaDados.getName());
}
ArrayList<MetaDados> presList = (ArrayList<MetaDados>) getIntent().getParcelableExtra("metaDadosPres");
for (MetaDados metaDados : presList) {
Log.d(Constants.DOWNLOAD_SELECTED_ACTIVITY, metaDados.getName());
}
Use Intent.putParcelableArrayListExtra() instead of putExtra(), and getParcelableArrayListExtra() instead of getParcelableExtra(). You can lose the casts as well, that may be where it's blowing up.