I have a problem with queries in RoomDatabase. I have a different queries and everything works good when I use SELECT statement with *. When I want to choose only name_product or amount it gives an error:
error: The columns returned by the query does not have the fields [id,amount,to_shop,cat_food,cat_package] in com.example.myapp.PojoClass even though they are annotated as non-null or primitive. Columns returned by the query: [name_product,amount]
LiveData<List<Items>> getItemNameAndAmount(int category);
One of my query:
#Query("SELECT name_product, amount FROM Table WHERE to_shop = 1 AND food_type = :category")
LiveData<List<Item_get>> getCategoryThingsToBuy(int category);
Problem is similar with this:
Android Room error: The columns returned by the query does not have the fields even though they are annotated as non-null or primitive
But I don't understand why this won't compile. Can somebody tell me?
In my PojoClass I have got a lot of different fields but only categories above are return as an error.
Why Android Studio doesn't throw me all of my Pojo fields as error? Not Only those above.
It throws errors in all queries statements not in one or two.
If it matter I have a empty constructor in my PojoClass.
EDIT:
My PojoClass
#Entity(tableName = "Table", indices = #Index(value = {"name_product"}, unique = true))
public class Item_get implements Parcelable {
#PrimaryKey(autoGenerate = true)
public int id;
#ColumnInfo(name = "name_product")
protected String name_product;
private String date_buy;
#ColumnInfo(name = "amount")
private int amount;
private Double price;
private Double value;
protected String date_exp;
protected String buy_place;
private String date_buy2;
private String buy_place2;
private Double price2;
private String date_buy3;
private String buy_place3;
private Double price3;
private int to_shop;
private int amount_toShop;
protected String actual_date;
private int cat_food;
protected int cat_package;
private int cat_packageToShop;
#TypeConverters(CategoriesConverter.class)
public Food_type food_type;
protected Item_get(Parcel in) {
id = in.readInt();
name_product = in.readString();
date_buy = in.readString();
amount = in.readInt();
if (in.readByte() == 0) {
price = null;
} else {
price = in.readDouble();
}
if (in.readByte() == 0) {
value = null;
} else {
value = in.readDouble();
}
date_exp = in.readString();
buy_place = in.readString();
date_buy2 = in.readString();
buy_place2 = in.readString();
if (in.readByte() == 0) {
price2 = null;
} else {
price2 = in.readDouble();
}
date_buy3 = in.readString();
buy_place3 = in.readString();
if (in.readByte() == 0) {
price3 = null;
} else {
price3 = in.readDouble();
}
to_shop = in.readInt();
actual_date = in.readString();
cat_food = in.readInt();
cat_package = in.readInt();
amount_toShop = in.readInt();
cat_packageToShop = in.readInt();
}
public static final Creator<Item_get> CREATOR = new Creator<Item_get>() {
#Override
public Item_get createFromParcel(Parcel in) {
return new Item_get(in);
}
#Override
public Item_get[] newArray(int size) {
return new Item_get[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name_product);
dest.writeString(date_buy);
dest.writeInt(amount);
if (price == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeDouble(price);
}
if (value == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeDouble(value);
}
dest.writeString(date_exp);
dest.writeString(buy_place);
dest.writeString(date_buy2);
dest.writeString(buy_place2);
if (price2 == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeDouble(price2);
}
dest.writeString(date_buy3);
dest.writeString(buy_place3);
if (price3 == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeDouble(price3);
}
dest.writeInt(to_shop);
dest.writeString(actual_date);
dest.writeInt(cat_food);
dest.writeInt(cat_package);
dest.writeInt(amount_toShop);
dest.writeInt(cat_packageToShop);
}
public enum Food_type{
SWEETS(1),
VEGETABLES_FRUITS(2),
DRINKS(3),
OTHER(4);
private int code;
Food_type(int code){
this.code = code;
}
public int getCode(){
return code;
}
}
#TypeConverters(PackageTypeConverter.class)
public Package_type package_type;
public enum Package_type{
PACKAGE_BOX(1),
PIECE(2),
OTHER_PACK(3);
private int code;
Package_type(int code){
this.code = code;
}
public int getCode(){
return code;
}
}
public Item_get(){
}
To short of my code I didn't paste all constructors beacuse I use different in each class.
Also I skipped getters and setters. If it is necessarily I will paste it.
I have objects in a firebase database which represent characters in a video game and contain all their stats (attack, defense, health etc). I use a firebaseListAdapter to fill a gridview with custom views showing the characters and some of their stats.
When using Android API 25 or below, this works as expected, but with API 26 and above, all data EXCEPT the health value come through fine, and the health value is always set to 0.
The health variable is simply an int, and is never treated any differently than the other integer values (strength, defense etc) anywhere in the code.
I'll note that adding a listener to the health value alone of any of the characters retrieves the value just fine.
Does anyone have any ideas what may be causing this?
FirebaseListOptions<PlayerMonster> options = new FirebaseListOptions.Builder<PlayerMonster>()
.setQuery(mDatabase.child("playerMonsters").child(firebaseId).orderByChild("size"), PlayerMonster.class)
.setLayout(R.layout.monster_grid_single)
.build();
monsterFirebaseAdapter = new FirebaseListAdapter<PlayerMonster>(options) {
#Override
protected void populateView(View view, PlayerMonster monsterInstance, int position) {
// Not getting value for health ONLY, max health and everything else works. Querying health by itself also works.
Timber.d("monster health is " + monsterInstance.getHealth() + ", max hp: " + monsterInstance.getMax_health());
// Code here populates the view
}
};
grid.setAdapter(monsterFirebaseAdapter);
package odyssey.game.balfur.odyssey.firebase_objects;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.firebase.database.Exclude;
import java.util.HashMap;
import java.util.Map;
public class PlayerMonster extends Monster implements Parcelable {
private String user_given_nick_name;
private int experience;
private int drawableRes;
private int itemHeldId;
protected PlayerMonster(Parcel in) {
user_given_nick_name = in.readString();
monster_name = in.readString();
itemHeldId = in.readInt();
type = in.readInt();
level = in.readInt();
health = in.readInt();
max_health = in.readInt();
experience = in.readInt();
size = in.readInt();
skill1 = in.readInt();
skill2 = in.readInt();
skill3 = in.readInt();
skill4 = in.readInt();
strength = in.readInt();
defense = in.readInt();
magicStrength = in.readInt();
magicDefense = in.readInt();
speed = in.readInt();
skinId = in.readInt();
}
public static final Creator<PlayerMonster> CREATOR = new Creator<PlayerMonster>() {
#Override
public PlayerMonster createFromParcel(Parcel in) {
return new PlayerMonster(in);
}
#Override
public PlayerMonster[] newArray(int size) {
return new PlayerMonster[size];
}
};
public int getDrawableRes() {return drawableRes;}
public void setDrawableRes(int drawableRes) {this.drawableRes = drawableRes;}
public int getExperience() {return experience;}
public void setExperience(int experience) {this.experience = experience; }
// public void addExperience(int experience) {this.experience += experience; }
public String getUser_given_nick_name() {
return user_given_nick_name;
}
public void setUser_given_nick_name(String user_given_nick_name) {this.user_given_nick_name = user_given_nick_name;}
public int getItemHeldId() {return itemHeldId;}
public void setItemHeldId(int itemHeldId) {this.itemHeldId = itemHeldId;}
public String getMonsterStringName(){ return user_given_nick_name; }
public PlayerMonster(){}
public PlayerMonster(String userGivenNickName, String monsterName, int itemHeldId, int type, int level, int health,
int max_health, int experience, int size, int skill1, int skill2, int skill3, int skill4,
int strength, int defense, int magicStrength, int magicDefense, int speed, int skinId){
this.user_given_nick_name = userGivenNickName;
this.monster_name = monsterName;
this.itemHeldId = itemHeldId;
this.type = type;
this.level = level;
this.health = health;
this.max_health = max_health;
this.experience = experience;
this.size = size;
this.skill1 = skill1;
this.skill2 = skill2;
this.skill3 = skill3;
this.skill4 = skill4;
this.strength = strength;
this.defense = defense;
this.magicStrength = magicStrength;
this.magicDefense = magicDefense;
this.speed = speed;
this.skinId = skinId;
}
// constructor for monsters w/ image resource. for use in the monsterBattleGridAdapter
public PlayerMonster(String userGivenNickName, String monsterName, int itemHeldId, int type, int level, int health,
int max_health, int experience, int size, int drawableRes, int skill1, int skill2, int skill3, int skill4,
int strength, int defense, int magicStrength, int magicDefense, int speed, int skinId){
this.user_given_nick_name = userGivenNickName;
this.monster_name = monsterName;
this.itemHeldId = itemHeldId;
this.type = type;
this.level = level;
this.health = health;
this.max_health = max_health;
this.experience = experience;
this.size = size;
this.drawableRes = drawableRes;
this.skill1 = skill1;
this.skill2 = skill2;
this.skill3 = skill3;
this.skill4 = skill4;
this.strength = strength;
this.defense = defense;
this.magicStrength = magicStrength;
this.magicDefense = magicDefense;
this.speed = speed;
this.skinId = skinId;
}
#Exclude
public Map<String, Object> toMap() {
HashMap<String, Object> result = new HashMap<>();
result.put("user_given_nick_name", user_given_nick_name);
result.put("monster_name", monster_name);
result.put("itemHeldId", itemHeldId);
result.put("type", type);
result.put("level", level);
result.put("health", health);
result.put("max_health", max_health);
result.put("experience", experience);
result.put("size", size);
result.put("skill1", skill1);
result.put("skill2", skill2);
result.put("skill3", skill3);
result.put("skill4", skill4);
result.put("strength", strength);
result.put("defense", defense);
result.put("magicStrength", magicStrength);
result.put("magicDefense", magicDefense);
result.put("speed", speed);
result.put("skinId", skinId);
return result;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(user_given_nick_name);
parcel.writeString(monster_name);
parcel.writeInt(itemHeldId);
parcel.writeInt(type);
parcel.writeInt(level);
parcel.writeInt(health);
parcel.writeInt(max_health);
parcel.writeInt(experience);
parcel.writeInt(size);
parcel.writeInt(skill1);
parcel.writeInt(skill2);
parcel.writeInt(skill3);
parcel.writeInt(skill4);
parcel.writeInt(strength);
parcel.writeInt(defense);
parcel.writeInt(magicStrength);
parcel.writeInt(magicDefense);
parcel.writeInt(speed);
parcel.writeInt(skinId);
}
}
package odyssey.game.balfur.odyssey.firebase_objects;
import android.os.Parcel;
import android.os.Parcelable;
import odyssey.game.balfur.odyssey.helpers.Condition;
import odyssey.game.balfur.odyssey.helpers.Helpers;
import odyssey.game.balfur.odyssey.helpers.TalentShorthand;
import java.util.HashMap;
import timber.log.Timber;
public class Monster implements Parcelable {
String monster_name;
int type;
int skill1;
int skill2;
int skill3;
int skill4;
int max_health;
int health;
int shield = 0;
int strength;
int speed;
int defense;
int magicStrength;
int magicDefense;
private boolean canSwap = true;
int skinId; // Each separate skin for each monster will have an ID associated with it
String firebaseId;
// At start of battle, record monsters stats to make sure buffs stay below certain %
public int[] baseStats = new int[6];
// Used to record how many stacks of a buff a monster has. can have between -5 and 5, each
// Representing 8% change from base stats. All values initialize to 0 by default
public int[] buffStacks = new int[6];
private HashMap<Integer, Condition> currentConditions = new HashMap<>();
private HashMap<Object, TalentShorthand> talentShorthand= new HashMap<>();
int level;
int size;
protected Monster(Parcel in) {
monster_name = in.readString();
type = in.readInt();
skill1 = in.readInt();
skill2 = in.readInt();
skill3 = in.readInt();
skill4 = in.readInt();
max_health = in.readInt();
health = in.readInt();
shield = in.readInt();
strength = in.readInt();
speed = in.readInt();
defense = in.readInt();
magicStrength = in.readInt();
magicDefense = in.readInt();
canSwap = in.readByte() != 0;
skinId = in.readInt();
firebaseId = in.readString();
baseStats = in.createIntArray();
buffStacks = in.createIntArray();
level = in.readInt();
size = in.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(monster_name);
dest.writeInt(type);
dest.writeInt(skill1);
dest.writeInt(skill2);
dest.writeInt(skill3);
dest.writeInt(skill4);
dest.writeInt(max_health);
dest.writeInt(health);
dest.writeInt(shield);
dest.writeInt(strength);
dest.writeInt(speed);
dest.writeInt(defense);
dest.writeInt(magicStrength);
dest.writeInt(magicDefense);
dest.writeByte((byte) (canSwap ? 1 : 0));
dest.writeInt(skinId);
dest.writeString(firebaseId);
dest.writeIntArray(baseStats);
dest.writeIntArray(buffStacks);
dest.writeInt(level);
dest.writeInt(size);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<Monster> CREATOR = new Creator<Monster>() {
#Override
public Monster createFromParcel(Parcel in) {
return new Monster(in);
}
#Override
public Monster[] newArray(int size) {
return new Monster[size];
}
};
// At start of battle, need to copy an array of each monsters stats and store it in each monster object
// These values will be checked when buff skills are used to ensure monsters stats do not go over the max buff amount (currently 40% over base)
public void backupStats(){
Timber.d("Backing up stats for %s", monster_name);
baseStats[0] = strength;
baseStats[1] = defense;
baseStats[2] = magicStrength;
baseStats[3] = magicDefense;
baseStats[4] = speed;
baseStats[5] = max_health;
}
public Monster(){}
public Monster(int level, String monster_name, int skinId){
this.level = level;
this.monster_name = monster_name;
this.skinId = skinId;
}
public int[] getBaseStats() { return baseStats; }
public void setBaseStats(int[] baseStats) { this.baseStats = baseStats; }
public String getFirebaseId() {return firebaseId;}
public void setFirebaseId(String firebaseId) {this.firebaseId = firebaseId;}
public int getSpeed() {return speed;}
public void setSpeed(int speed) {this.speed = speed;}
public int getStrength() {return strength;}
public void setStrength(int strength) {this.strength = strength;}
public int getDefense() {return defense;}
public void setDefense(int defense) {this.defense = defense;}
public int getMagicStrength() {return magicStrength;}
public void setMagicStrength(int magicStrength) {this.magicStrength = magicStrength;}
public int getMagicDefense() {return magicDefense;}
public void setMagicDefense(int magicDefense) {this.magicDefense = magicDefense;}
public int getSkill2() {return skill2;}
public void setSkill2(int skill2) {this.skill2 = skill2;}
public int getSkill3() {return skill3;}
public void setSkill3(int skill3) {this.skill3 = skill3;}
public int getSkill1() {return skill1;}
public void setSkill1(int skill1) {this.skill1 = skill1;}
public int getSkill4() {return skill4;}
public void setSkill4(int skill4) {this.skill4 = skill4;}
public int getSize() {return size;}
public void setSize(int size) {this.size = size;}
public int getMax_health() {
return max_health;
}
public void setMax_health(int max_health) {
this.max_health = max_health;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
if (health > max_health) this.health = max_health;
else if (health <= 0) this.health = 0;
else this.health = health;
}
public int getShield() {return shield;}
public void setShield(int shield) {
if (shield > max_health) this.shield = max_health;
else if (shield <= 0) this.shield = 0;
else this.shield = shield;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getMonster_name() {
return monster_name;
}
public void setMonster_name(String monster_name) {
this.monster_name = monster_name;
}
public String getMonsterStringName(){ return monster_name; }
public int getSkinId() { return skinId; }
public void setSkinId(int skinId) { this.skinId = skinId; }
public boolean isCanSwap() { return canSwap; }
public void setCanSwap(boolean canSwap) { this.canSwap = canSwap; }
public HashMap<Object, TalentShorthand> getTalentShorthand(){
return talentShorthand;
}
public void setTalentShorthand(HashMap<Object, TalentShorthand> talentShorthand){this.talentShorthand = talentShorthand;}
public HashMap<Integer, Condition> getCurrentConditions(){
return currentConditions;
}
public void setCurrentConditions(HashMap<Integer, Condition> currentConditions){this.currentConditions = currentConditions;}
public void addCondition(int conditionId, int roundsRemaining, double conditionDamagePercent, int addedStacks){
Condition condition;
if (currentConditions.containsKey(conditionId)){
int conditionStacks = currentConditions.get(conditionId).getConditionStacks();
// If adding one to the current stacks would still be less than the max stacks for that condition, add 1, otherwise do nothing
if (conditionStacks + addedStacks > Helpers.getConditionMaxStacks(conditionId)){
conditionStacks = Helpers.getConditionMaxStacks(conditionId);
addedStacks = Helpers.getConditionMaxStacks(conditionId) - conditionStacks; // The difference between the max and the preexisting conditions will be the true number of stacks added
} else {
conditionStacks += addedStacks;
}
condition = new Condition(roundsRemaining, conditionDamagePercent, conditionStacks);
condition.setStacksAppliedThisRound(addedStacks);
currentConditions.put(conditionId, condition);
} else {
condition = new Condition(roundsRemaining, conditionDamagePercent, addedStacks);
condition.setStacksAppliedThisRound(addedStacks);
currentConditions.put(conditionId, condition);
}
}
public void removeCondition(int conditionId){ // Used for calling condition remove skills doi
if (currentConditions.containsKey(conditionId)){
currentConditions.remove(conditionId);
}
}
}
I ended up finding a fix, but I'm still uncertain as to why this was an issue in only certain android API levels.
public void setHealth(int health) {
if (health > max_health) this.health = max_health;
else if (health <= 0) this.health = 0;
else this.health = health;
}
The setHealth() function is called for each object when firebaseListAdapter gets these objects, and when using API 26+, these functions must be called piecemeal or something because when called, the max_health value is set to 0, so the condition
if (health > max_health)
is true, and therefore health was being erroneously set to 0. In older versions of Android, the max_health value is already set correctly by the tim the setHealth() function is called.
I added a check to ensure that max_health isn't 0 at the top of the setHealth() function, as a character can't have 0 for a max_health
I'd still be interested for curiosity's sake and to possibly help someone down the line in learning why the behavior differs in the different versions of Android, but for the time being I'll consider my original question answered.
I am using parcelable to pass class to new activity. However, at receiving side, I get empty values. It is strange that I get empty values and not null values. However, I do expect some values in each attribute. I have checked that the attributes are not empty at the caller part. Why is this so?
Here is my implementation:
Call new activity
Intent intent = new Intent(ProductDetailsActivity.this, ShopActivity.class);
intent.putExtra(ShopActivity.KEY_COMPANY, products.get(currentTypePosition).companyDetails);
startActivity(intent);
Receive
Company company = getIntent().getParcelableExtra(KEY_COMPANY);
Parcelable
public class Company implements Parcelable {
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Company createFromParcel(Parcel in) {
return new Company(in);
}
public Company[] newArray(int size) {
return new Company[size];
}
};
// TODO: Remove default values once backend is completed
public ArrayList<CompanyProduct> companyProducts = getDefaultCompanyProducts();
public String name = "hello";
public int icon = 0;
public int productCount = 100;
public int followerCount = 9000;
public String location = "hello";
public String contact = "400-23729";
public String description = "hello";
public int storeCount = 100;
public Company() {
super();
}
public Company(Parcel in) {
in.readTypedList(companyProducts, Status.CREATOR);
name = in.readString();
icon= in.readInt();
productCount = in.readInt();
followerCount = in.readInt();
location = in.readString();
contact = in.readString();
description = in.readString();
storeCount = in.readInt();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(companyProducts);
dest.writeString(name);
dest.writeInt(icon);
dest.writeInt(productCount);
dest.writeInt(followerCount);
dest.writeString(location);
dest.writeString(contact);
dest.writeString(description);
dest.writeInt(storeCount);
}
}
I want to pass an object of type Annonce to an Intent. As you can see, it's simple if the class attributes are primitives, However; in my case I have an image (Bitmap) and an attribute of type Client ( I have created a Client class).
My solution is to access the Client attributes (using getter and setter) and parsing it in the writeToParcel method one by one (it takes too much time), and for the image, I am sending it in the mainActivity using ByteArrayOutputStream. Can anyone help me do it all in Annonce class.
public class Annonce implements Parcelable {
String article, desc, temps, ville, categorie;
int prix;
Bitmap img;
Client c;
public Annonce(String article, String desc, String temps, String ville,
String categorie, int prix, Bitmap img, Client c) {
this.article = article;
this.desc = desc;
this.c = c;
this.prix = prix;
this.img = img;
this.temps = temps;
this.categorie = categorie;
this.ville = ville;
}
public static final Parcelable.Creator<Annonce> CREATOR = new Parcelable.Creator<Annonce>() {
public Annonce createFromParcel(Parcel source) {
return new Annonce(source);
}
public Annonce[] newArray(int size) {
return new Annonce[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(article);
parcel.writeString(desc);
parcel.writeString(temps);
parcel.writeString(ville);
parcel.writeString(categorie);
parcel.writeInt(prix);
}
public Annonce(Parcel source) {
article = source.readString();
desc = source.readString();
temps = source.readString();
ville = source.readString();
categorie = source.readString();
prix = source.readInt();
}
}
Having an attribut of type "bitmap" is not a good solution . Instead of that , we can use the path of the image to refer to the bitmap image .
Also, we can convert the Client into object in parcelable in order to send it through intent.
I can't see the error, I'm having this problem for a long time already... My parcelable class crashes if it is recreated, but I can't find the problem...
I checked the order of writing/reading data.
I checked the functions I use (direct reading/writing vs my custum null save functions)
I marked the line in the first code block that created following exception: java.lang.ArrayIndexOutOfBoundsException: length=5; index=5
SHORT CODE
private Set<ContactType> mMatchesDataLoaded = new HashSet<ContactType>();
saving the set
dest.writeInt(mMatchesDataLoaded.size());
Iterator<ContactType> it = mMatchesDataLoaded.iterator();
while (it.hasNext())
dest.writeInt(it.next().ordinal());
reading the set
int count = source.readInt();
for (int i = 0; i < count; i++)
// ---------------------------------------------------------------------------
// next line produces EXCEPTION!!! java.lang.ArrayIndexOutOfBoundsException: length=5; index=5
// ---------------------------------------------------------------------------
mMatchesDataLoaded.add(ContactType.values()[source.readInt()]);
FULL CODE
I can't see any problems or rather where the problem is...
public class ContactPhone implements Parcelable
{
public static enum ContactType
{
WhatsApp,
Viber,
GooglePlus,
Twitter,
Instagram
}
private boolean mIsUserProfile = false;
private boolean mHasImage;
private int mId;
private long mRawId;
private String mName = null;
private List<PhoneNumber> mNumbers = new ArrayList<PhoneNumber>();
private DBPhoneContact mDBContact = null;
private Set<ContactType> mMatchesDataLoaded = new HashSet<ContactType>();
private HashMap<ContactType, List<BaseMatchContact>> mMatchesData = new HashMap<ContactType, List<BaseMatchContact>>();
// ----------------------
// Parcelable
// ----------------------
#Override
public void writeToParcel(Parcel dest, int flags)
{
ParcelBundleUtils.writeBoolean(dest, mIsUserProfile);
ParcelBundleUtils.writeBoolean(dest, mHasImage);
ParcelBundleUtils.writeIntegerNullSafe(dest, mId);
ParcelBundleUtils.writeLongNullSafe(dest, mRawId);
ParcelBundleUtils.writeStringNullSafe(dest, mName);
dest.writeList(mNumbers);
ParcelBundleUtils.writeLongNullSafe(dest, mDBContact != null ? mDBContact.getId() : null);
// save set
dest.writeInt(mMatchesDataLoaded.size());
Iterator<ContactType> it = mMatchesDataLoaded.iterator();
while (it.hasNext())
dest.writeInt(it.next().ordinal());
// save HashMap
dest.writeInt(mMatchesData.size());
for (Map.Entry<ContactType, List<BaseMatchContact>> entry : mMatchesData.entrySet())
{
dest.writeInt(entry.getKey().ordinal());
dest.writeInt(entry.getValue().size());
for (int i = 0; i < entry.getValue().size(); i++)
dest.writeParcelable(entry.getValue().get(i), 0);
}
}
public void readFromParcel(Parcel source)
{
mIsUserProfile = ParcelBundleUtils.readBoolean(source);
mHasImage = ParcelBundleUtils.readBoolean(source);
mId = ParcelBundleUtils.readIntegerNullSafe(source);
mRawId = ParcelBundleUtils.readLongNullSafe(source);
mName = ParcelBundleUtils.readStringNullSafe(source);
source.readList(mNumbers, PhoneNumber.class.getClassLoader());
Long id = ParcelBundleUtils.readLongNullSafe(source);
mDBContact = null;
if (id != null)
mDBContact = MainApp.getDS().getDBPhoneContactDao().load(id);
// read set
int count = source.readInt();
for (int i = 0; i < count; i++)
// ---------------------------------------------------------------------------
// next line produces EXCEPTION!!! java.lang.ArrayIndexOutOfBoundsException: length=5; index=5
// ---------------------------------------------------------------------------
mMatchesDataLoaded.add(ContactType.values()[source.readInt()]);
// read HashMap
count = source.readInt();
for (int i = 0; i < count; i++)
{
ContactType type = ContactType.values()[source.readInt()];
Class<?> clazz = BaseDef.getMatchClass(type);
// L.d(this, "Classloader: " + clazz.getName() + " type: " + type.name());
int size = source.readInt();
List<BaseMatchContact> list = new ArrayList<BaseMatchContact>();
for (int j = 0; j < size; j++)
list.add((BaseMatchContact) source.readParcelable(clazz.getClassLoader()));
mMatchesData.put(type, list);
}
}
}
The PhoneNumber class implements parcelable and is quite simple and read/writes like following:
#Override
public void writeToParcel(Parcel dest, int flags)
{
ParcelBundleUtils.writeStringNullSafe(dest, mName);
ParcelBundleUtils.writeStringNullSafe(dest, mNormNumber);
ParcelBundleUtils.writeStringNullSafe(dest, mNumber);
}
public void readFromParcel(Parcel source)
{
mName = ParcelBundleUtils.readStringNullSafe(source);
mNormNumber = ParcelBundleUtils.readStringNullSafe(source);
mNumber = ParcelBundleUtils.readStringNullSafe(source);
}
And here are my helper functions:
public static void writeBoolean(Parcel p, boolean b)
{
p.writeByte((byte) (b ? 1 : 0));
}
public static boolean readBoolean(Parcel p)
{
return p.readByte() == 1;
}
public static void writeStringNullSafe(Parcel p, String s)
{
p.writeByte((byte) (s != null ? 1 : 0));
if (s != null)
p.writeString(s);
}
public static void writeIntegerNullSafe(Parcel p, Integer i)
{
p.writeByte((byte) (i != null ? 1 : 0));
if (i != null)
p.writeInt(i);
}
public static void writeLongNullSafe(Parcel p, Long l)
{
p.writeByte((byte) (l != null ? 1 : 0));
if (l != null)
p.writeLong(l);
}
public static void writeDoubleNullSafe(Parcel p, Double d)
{
p.writeByte((byte) (d != null ? 1 : 0));
if (d != null)
p.writeDouble(d);
}
public static void writeParcelableNullSafe(Parcel p, Parcelable d, int flags)
{
p.writeByte((byte) (d != null ? 1 : 0));
if (d != null)
p.writeParcelable(d, flags);
}
public static String readStringNullSafe(Parcel p)
{
boolean isPresent = p.readByte() == 1;
return isPresent ? p.readString() : null;
}
public static Integer readIntegerNullSafe(Parcel p)
{
boolean isPresent = p.readByte() == 1;
return isPresent ? p.readInt() : null;
}
public static Long readLongNullSafe(Parcel p)
{
boolean isPresent = p.readByte() == 1;
return isPresent ? p.readLong() : null;
}
public static Double readDoubleNullSafe(Parcel p)
{
boolean isPresent = p.readByte() == 1;
return isPresent ? p.readDouble() : null;
}
#SuppressWarnings("unchecked")
public static <T extends Parcelable> T readParcelableNullSafe(Parcel p, ClassLoader classLoader)
{
boolean isPresent = p.readByte() == 1;
return isPresent ? (T) p.readParcelable(classLoader) : null;
}
int count = source.readInt(); // index is raised + 1
for (int i = 0; i < count; i++)
mMatchesDataLoaded.add(ContactType.values()[source.readInt()]); // index is raised by 1, starting with 1!
you loop from 0 to 4, but source.readInt() was already called once, so you called it 5 times in total.
ContactType contains 5 values, from index 0 to index 4. You are trying to access the index 5, which does not exist.
mMatchesDataLoaded.add(ContactType.values()[source.readInt()]);
source.readInt() gives you a 5, try to figure out, with debug, why does it contain this value.
My guess is that writeToParcel writes this 5, try to inspect mMatchesDataLoaded which maybe contains some additional unwanted data.