Alok Saini Alok Saini - 3 months ago 13
Java Question

Android Java objModelClass.getClass().getDeclaredFields() returns "$change" as one field

In our project we are using data models to store data received from services. So in a particular case when we are inserting data to database, we use objModelClass.getClass().getDeclaredFields() method to get all field names, and it is correctly returning all field names of the class but also returning one extra field with name "$change", which does not exists in the class.

Strange thing is, In android studio there was no such problem but when we upgraded to android studio 2.0 this happened.

Though i have applied a quick fix, but i need to properly fix this.
I want to know the why is it happening?

This the function using this method

public void insertValuesIntoTable(final String strTableName, ArrayList<Object> alObjClasses,
String strPrimaryKey, String strCompositeKey) {
try {
SQLiteDatabase db_w = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
//Iterate through every model class Object in the ArrayList and transfer the contents to the DB
if (alObjClasses != null)
for (Object objModelClass : alObjClasses) {
for (Field field : objModelClass.getClass().getDeclaredFields()) {
//Encrypt value if encryption is enabled & the column is to be encrypted
try {
field.setAccessible(true);
//Test if the table received is Ignore contacts. Minor Hack inserted
//So that the same contact data model can be re used. It checks if the
//Table is of ignored constant or not then checks if the column exists
//in the table or not as that of the field name. Doing this saves
//from receiving a SQLConstraint exception stating, "Column not found"
if(field.getName().equals("$change"))
continue;
if(strTableName.equalsIgnoreCase(ContactConstants.TABLE_IGNORED_CONTACTS))
if(!field.getName().equalsIgnoreCase(ContactConstants.TABLE_IGNORED_CONTACTS_NAMES)
&& !field.getName().equalsIgnoreCase(ContactConstants.TABLE_IGNORED_CONTACTS_NUMBERS)
&& !field.getName().equalsIgnoreCase(ContactConstants.TABLE_IGNORED_CONTACTS_EMAIL)
&& !field.getName().equalsIgnoreCase(ContactConstants.TABLE_IGNORED_CONTACTS_CITY)
&& !field.getName().equalsIgnoreCase(ContactConstants.TABLE_IGNORED_CONTACTS_ID))
continue;
contentValues.put(field.getName(),
(Constants.ENCRYPTION_PREFERENCE && HelperFunctions.isColumnEncrypted(field.getName()))
? HelperFunctions.encryptString((String) field.get(objModelClass))
: (String) field.get(objModelClass));
} catch (IllegalAccessException e) {
// e.printStackTrace();
//Never thrown since field.setAccessible(true); is called before accessing data
}
catch ( ClassCastException e) {
// e.printStackTrace();
//Never thrown since field.setAccessible(true); is called before accessing data
}
}
try {
if (db_w.insert(strTableName, null, contentValues) == -1)
throw new SQLiteConstraintException();
} catch (SQLiteConstraintException e) {
// e.printStackTrace();
Log.i("Error - DB", "Error occurred while trying to add data to "
+ strTableName + " Table, updating data instead.");
//Since the entry exists in the DB, it will be updated instead
updateEntryInDatabase(db_w, strTableName, strPrimaryKey, strCompositeKey, contentValues);
}
}
db_w.close();
} catch (NullPointerException e) {
// e.printStackTrace();
//Is thrown sometimes when the context of the activity which called it is destroyed mid execution
}
catch (SQLiteException e) {
// e.printStackTrace();
//Is thrown sometimes when the context of the activity which called it is destroyed mid execution
}
}

Answer

I'm struggling with this issue since this morning. It's due to the new functionality of Android Studio 2, Instant run, that adds this field somewhere, as it may be seen while debugging: it implements the interface com.android.tools.fd.runtime.IncrementalChange.

This issue can be solved by checking the field using the isSynthetic() method: it will return true for fields generated by the runtime like $change, and false otherwise.