Reputation: 2392
I'm getting this error when trying to pass a Parcelable
through an Intent
.
Basically, I've implemented my class as Parcelable
, but I can't pass the object to another activity because it crashes when reading it. I tried several answers for this problem, but none has helped me to solve this issue.
So far, this is my code for WeatherData.java
(sorry, the class is a bit large):
public class WeatherData implements Parcelable {
private final static String ICON_ADDR = "http://openweathermap.org/img/w/";
static class City implements Parcelable {
String name;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
Bundle bundle = new Bundle();
bundle.putString("cityName", name);
dest.writeBundle(bundle);
}
public static final Parcelable.Creator<City> CREATOR = new Creator<City>() {
@Override
public City createFromParcel(Parcel source) {
// read the bundle containing key value pairs from the parcel
Bundle bundle = source.readBundle();
// instantiate the forecast info using values from the bundle
return new City(bundle.getString("cityName"));
}
@Override
public City[] newArray(int size) {
return new City[size];
}
};
private City (String cityName) {
name = cityName;
}
}
static class Weather implements Parcelable {
String description;
String icon;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
Bundle bundle = new Bundle();
bundle.putString("description", description);
bundle.putString("icon", icon);
dest.writeBundle(bundle);
}
public static final Parcelable.Creator<Weather> CREATOR = new Creator<Weather>() {
@Override
public Weather createFromParcel(Parcel source) {
// read the bundle containing key value pairs from the parcel
Bundle bundle = source.readBundle();
// instantiate the forecast info using values from the bundle
return new Weather(bundle.getString("description"),
bundle.getString("icon"));
}
@Override
public Weather[] newArray(int size) {
return new Weather[size];
}
};
private Weather (String description, String icon) {
icon = icon;
description = description;
}
}
static class Temp implements Parcelable {
float day;
float min;
float max;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
Bundle bundle = new Bundle();
bundle.putFloat("day", day);
bundle.putFloat("min", min);
bundle.putFloat("max", max);
dest.writeBundle(bundle);
}
public static final Parcelable.Creator<Temp> CREATOR = new Creator<Temp>() {
@Override
public Temp createFromParcel(Parcel source) {
// read the bundle containing key value pairs from the parcel
Bundle bundle = source.readBundle();
// instantiate the forecast info using values from the bundle
return new Temp(bundle.getFloat("day"),
bundle.getFloat("min"), bundle.getFloat("max"));
}
@Override
public Temp[] newArray(int size) {
return new Temp[size];
}
};
private Temp (float tDay, float tMin, float tMax) {
day = tDay;
min = tMin;
max = tMax;
}
}
static class ForecastInfo implements Parcelable {
Temp temp;
ArrayList<Weather> weather;
String getTemperatureInCelsius() {
float t = temp.day - 273.15f;
return String.format("%.0f" + (char) 0x00B0, t);
};
public String getIconAddress() {
return ICON_ADDR + weather.get(0).icon + ".png";
};
public String getDescription() {
if (weather != null && weather.size() > 0)
return weather.get(0).description;
return null;
};
@Override
public int describeContents() {
return 0;
};
@Override
public void writeToParcel(Parcel dest, int flags) {
Bundle bundle = new Bundle();
bundle.putParcelable("temp", this.temp);
bundle.putParcelableArrayList("weatherList", this.weather);
dest.writeBundle(bundle);
};
public static final Parcelable.Creator<ForecastInfo> CREATOR = new Creator<ForecastInfo>() {
@Override
public ForecastInfo createFromParcel(Parcel source) {
// read the bundle containing key value pairs from the parcel
Bundle bundle = source.readBundle();
// instantiate the forecast info using values from the bundle
return new ForecastInfo((Temp) bundle.get("temp"),
(ArrayList<Weather>) bundle.get("weatherList"));
}
@Override
public ForecastInfo[] newArray(int size) {
return new ForecastInfo[size];
}
};
private ForecastInfo (Temp temp, ArrayList<Weather> weatherList) {
this.temp = temp;
this.weather = weatherList;
};
}
// Relevant data for WeatherData
// The city (which only has a name in it)
City city;
// The array of forecast info extracted from the JSON
ArrayList<ForecastInfo> list;
private WeatherData (City city, ArrayList<ForecastInfo> forecastList) {
city = city;
list = forecastList;
}
// A method that converts temperature from Kelvin degrees to Celsius
String getTemperatureInCelsius(int day) {
return list.get(day).getTemperatureInCelsius();
}
// getIconAddress concatenates the base address and the specific code for
// the icon
public String getIconAddress(int day) {
return list.get(day).getIconAddress();
}
public String getName() {
return city.name;
}
public String getDescription(int day) {
return list.get(day).getDescription();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// create a bundle for the key value pairs
Bundle bundle = new Bundle();
// insert the key value pairs to the bundle
bundle.putString("cityName", this.getName());
bundle.putParcelableArrayList("forecastList", this.list);
// write the key value pairs to the parcel
dest.writeBundle(bundle);
}
public static final Parcelable.Creator<WeatherData> CREATOR = new Creator<WeatherData>() {
@Override
public WeatherData createFromParcel(Parcel source) {
// read the bundle containing key value pairs from the parcel
Bundle bundle = source.readBundle();
return new WeatherData(bundle.getString("cityName"),
new ArrayList<ForecastInfo>());
}
@Override
public WeatherData[] newArray(int size) {
return new WeatherData[size];
}
};
private WeatherData (String name, ArrayList<ForecastInfo> forecastInfos) {
City cityNew = new City(name);
city = cityNew;
forecastInfos.add(new ForecastInfo(
new Temp(1.0f, 1.0f, 1.0f),
new ArrayList<Weather>()));
list = forecastInfos;
}
private WeatherData (String name) {
City cityNew = new City(name);
city = cityNew;
}
}
Then, in my MainActivity
, I call the following function (sorry about the multiple calls to setExtrasClassLoader
):
public void showDetails(View view) {
Intent intent = new Intent(getApplicationContext(), ShowDetailsActivity.class);
intent.setExtrasClassLoader(WeatherData.class.getClassLoader());
intent.setExtrasClassLoader(WeatherData.ForecastInfo.class.getClassLoader());
intent.setExtrasClassLoader(WeatherData.City.class.getClassLoader());
intent.setExtrasClassLoader(WeatherData.Weather.class.getClassLoader());
intent.setExtrasClassLoader(WeatherData.Temp.class.getClassLoader());
intent.putExtra("bundleObject", weatherData.list);
startActivity(intent);
}
After that, I'm trying to get the content of the intent in the new activity inside the onCreate
method by:
Intent intent = getIntent();
intent.setExtrasClassLoader(WeatherData.class.getClassLoader());
intent.setExtrasClassLoader(WeatherData.ForecastInfo.class.getClassLoader());
ArrayList<WeatherData.ForecastInfo> list = intent.getParcelableArrayListExtra("bundleObject");
If I pass weatherData.getName()
instead of weatherData.list
in showDetails
, it works but just for the string name of the city. When I try to put an ArrayList<ForecastInfo>
, it breaks and raises this error:
Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.example.android.climapp.WeatherData$Temp at android.os.Parcel.readParcelableCreator(Parcel.java:2411) at android.os.Parcel.readParcelable(Parcel.java:2337) ...
Upvotes: 1
Views: 695
Reputation: 2392
Solved!
To anyone who is struggling with this issue, it's because the new Bundle
inside the createFromParcel
method doesn't know about the ForecastInfo
class. Thus, it could be solved telling the bundle to load the data of the missing class through setClassLoader
like this:
@Override
public WeatherData createFromParcel(Parcel source) {
// read the bundle containing key value pairs from the parcel
Bundle bundle = source.readBundle();
bundle.setClassLoader(ForecastInfo.class.getClassLoader());
// instantiate the weather using values from the bundle
bundle.getParcelable("forecastList"));
return new WeatherData(bundle.getString("cityName"),
new ArrayList<ForecastInfo>());
}
Upvotes: 2