Si vous utilisez la suite Proguard
-keepnames public class * extends io.realm.RealmModel
-keep public class * extends io.realm.RealmModel { *; }
-keepnames public class * extends io.realm.RealmObject
-keep public class * extends io.realm.RealmObject { *; }
-keepattributes *Annotation*
Ensuite, utilisez le code suivant
public class AutoMigration
implements RealmMigration {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MigrationIgnore {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MigratedField {
FieldAttribute[] fieldAttributes();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MigratedLink {
Class<? extends RealmModel> linkType(); // RealmList<T extends RealmModel> is nice, but T cannot be obtained through reflection.
}
@Override
public int hashCode() {
return AutoMigration.class.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj != null && obj instanceof AutoMigration;
}
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmConfiguration realmConfiguration = realm.getConfiguration();
Set<Class<? extends RealmModel>> latestRealmObjectClasses = realmConfiguration.getRealmObjectClasses();
RealmSchema realmSchema = realm.getSchema();
Set<RealmObjectSchema> initialObjectSchemas = realmSchema.getAll();
// first we must create any object schema that belongs to model class that is not part of the schema yet, to allow links.
List<RealmObjectSchema> createdObjectSchemas = new LinkedList<>();
// first we must check for classes that are in the schema, but are not in the configuration.
Set<String> modelClassNames = new LinkedHashSet<>();
Map<String, Class<? extends RealmModel>> modelClassNameToClassMap = new LinkedHashMap<>();
Set<String> schemaClassNames = new LinkedHashSet<>();
Map<String, RealmObjectSchema> schemaClassNameToObjectSchemaMap = new LinkedHashMap<>();
for(Class<? extends RealmModel> modelClass : latestRealmObjectClasses) {
modelClassNames.add(modelClass.getSimpleName()); // "Cat", requires `-keepnames public class * extends io.realm.RealmObject`
modelClassNameToClassMap.put(modelClass.getSimpleName(), modelClass);
}
for(RealmObjectSchema objectSchema : initialObjectSchemas) {
schemaClassNames.add(objectSchema.getClassName()); // "Cat", requires `-keepnames public class * extends io.realm.RealmObject`
schemaClassNameToObjectSchemaMap.put(objectSchema.getClassName(), objectSchema);
}
// now we must check if the model contains classes that are not part of the schema.
for(String modelClassName : modelClassNames) {
if(!schemaClassNames.contains(modelClassName)) {
// the model class is not part of the schema, we must add it to the schema.
RealmObjectSchema objectSchema = realmSchema.create(modelClassName);
createdObjectSchemas.add(objectSchema);
}
}
// we must check if existing schema classes have changed fields, or if they were removed from the model.
for(String objectClassName : schemaClassNames) {
RealmObjectSchema objectSchema = schemaClassNameToObjectSchemaMap.get(objectClassName);
if(modelClassNames.contains(objectClassName)) {
// the model was found in the schema, we must match their fields.
Class<? extends RealmModel> modelClass = modelClassNameToClassMap.get(objectClassName);
matchFields(realmSchema, objectSchema, modelClass);
} else {
// the model class was not part of the schema, so we must remove the object schema.
realmSchema.remove(objectClassName);
}
}
// now that we've set up our classes, we must also match the fields of newly created schema classes.
for(RealmObjectSchema createdObjectSchema : createdObjectSchemas) {
Class<? extends RealmModel> modelClass = modelClassNameToClassMap.get(createdObjectSchema.getClassName());
matchFields(realmSchema, createdObjectSchema, modelClass);
}
}
private void matchFields(RealmSchema realmSchema, RealmObjectSchema objectSchema, Class<? extends RealmModel> modelClass) {
Field[] allModelFields = modelClass.getDeclaredFields();
Set<String> modelFieldNames = new LinkedHashSet<>(allModelFields.length);
Map<String, Field> modelFieldNameToFieldMap = new LinkedHashMap<>(allModelFields.length);
for(Field field : allModelFields) {
modelFieldNames.add(field.getName());
modelFieldNameToFieldMap.put(field.getName(), field);
}
Set<String> schemaFieldNames = objectSchema.getFieldNames(); // field names require `-keep public class * extends io.realm.RealmObject { *; }`
for(String schemaFieldName : schemaFieldNames) {
if(!modelFieldNames.contains(schemaFieldName)) {
// the model does not contain this field, so it no longer exists. We must remove this field.
objectSchema.removeField(schemaFieldName);
}
}
for(String modelFieldName : modelFieldNames) {
Field field = modelFieldNameToFieldMap.get(modelFieldName);
if(Modifier.isStatic(field.getModifiers())) { // we must ignore static fields!
continue;
}
if(Modifier.isTransient(field.getModifiers())) { // transient fields are ignored.
continue;
}
if(field.isAnnotationPresent(MigrationIgnore.class)) {
continue; // manual ignore.
}
Class<?> fieldType = field.getType();
if(!schemaFieldNames.contains(modelFieldName)) {
// the schema does not contain the model's field, we must add this according to type!
if(isNonNullPrimitive(fieldType) || isPrimitiveObjectWrapper(fieldType) || isFieldRegularObjectType(fieldType)) {
objectSchema.addField(modelFieldName, fieldType);
} else {
if(fieldType == RealmResults.class) { // computed field (like @LinkingObjects), so this should be ignored.
//noinspection UnnecessaryContinue
continue;
} else if(fieldType == RealmList.class) {
// TODO: value lists in 4.0.0!
MigratedLink migratedLink = field.getAnnotation(MigratedLink.class);
if(migratedLink == null) {
throw new IllegalStateException("Link list [" + field.getName() + "] cannot be added to the schema without @MigratedLink(linkType) annotation.");
}
Class<? extends RealmModel> linkObjectClass = migratedLink.linkType();
String linkedObjectName = linkObjectClass.getSimpleName();
RealmObjectSchema linkedObjectSchema = realmSchema.get(linkedObjectName);
if(linkedObjectSchema == null) {
throw new IllegalStateException("The object schema [" + linkedObjectName + "] defined by link [" + modelFieldName + "] was not found in the schema!");
}
objectSchema.addRealmListField(field.getName(), linkedObjectSchema);
} else {
if(!RealmModel.class.isAssignableFrom(fieldType)) {
continue; // this is most likely an @Ignore field, let's just ignore it
}
String linkedObjectName = field.getType().getSimpleName();
RealmObjectSchema linkedObjectSchema = realmSchema.get(linkedObjectName);
if(linkedObjectSchema == null) {
throw new IllegalStateException("The object schema [" + linkedObjectName + "] defined by field [" + modelFieldName + "] was not found in the schema!");
}
objectSchema.addRealmObjectField(field.getName(), linkedObjectSchema);
}
}
}
// even if it's added, its attributes might be mismatched! This must happen both if newly added, or if already exists.
if(isNonNullPrimitive(fieldType) || isPrimitiveObjectWrapper(fieldType) || isFieldRegularObjectType(fieldType)) {
matchMigratedField(objectSchema, modelFieldName, field);
}
}
}
private void matchMigratedField(RealmObjectSchema objectSchema, String modelFieldName, Field field) {
MigratedField migratedField = field.getAnnotation(MigratedField.class); // @Required is not kept alive by its RetentionPolicy. We must use our own!
if(migratedField != null) {
boolean isIndexed = false;
boolean isRequired = false;
boolean isPrimaryKey = false;
for(FieldAttribute fieldAttribute : migratedField.fieldAttributes()) {
if(fieldAttribute == FieldAttribute.INDEXED) {
isIndexed = true;
} else if(fieldAttribute == FieldAttribute.REQUIRED) {
isRequired = true;
} else if(fieldAttribute == FieldAttribute.PRIMARY_KEY) {
isPrimaryKey = true;
}
}
if(isPrimaryKey && !objectSchema.isPrimaryKey(modelFieldName)) {
if(objectSchema.hasPrimaryKey()) {
throw new UnsupportedOperationException(
"Multiple primary keys are not supported: [" + objectSchema
.getClassName() + " :: " + modelFieldName + "]");
}
objectSchema.addPrimaryKey(modelFieldName);
}
if(!isPrimaryKey && objectSchema.isPrimaryKey(modelFieldName)) {
objectSchema.removePrimaryKey();
}
// index management must be after primary key because removePrimaryKey() removes index as well.
if((isIndexed || isPrimaryKey) && !objectSchema.hasIndex(modelFieldName)) {
objectSchema.addIndex(modelFieldName);
}
if(!isIndexed && !isPrimaryKey /* primary key is indexed by default! */ && objectSchema.hasIndex(modelFieldName)) {
objectSchema.removeIndex(modelFieldName);
}
if(isNonNullPrimitive(field.getType())) {
if(!objectSchema.isRequired(modelFieldName)) {
objectSchema.setNullable(modelFieldName, false);
}
} else {
if(isRequired && objectSchema.isNullable(modelFieldName)) {
objectSchema.setNullable(modelFieldName, false);
}
if(!isRequired && !objectSchema.isNullable(modelFieldName)) {
objectSchema.setNullable(modelFieldName, true);
}
}
}
}
private boolean isFieldRegularObjectType(Class<?> fieldType) {
return fieldType == String.class || fieldType == Date.class || fieldType == byte[].class;
}
private boolean isPrimitiveObjectWrapper(Class<?> fieldType) {
return fieldType == Boolean.class //
|| fieldType == Byte.class || fieldType == Short.class || fieldType == Integer.class || fieldType == Long.class //
|| fieldType == Float.class || fieldType == Double.class;
}
private boolean isNonNullPrimitive(Class<?> fieldType) {
return fieldType == boolean.class //
|| fieldType == byte.class || fieldType == short.class || fieldType == int.class || fieldType == long.class //
|| fieldType == float.class || fieldType == double.class;
}
}
Ensuite, vous annoter vos champs pour lorsque la classe de modèle a @PrimaryKey
ou @Ignore
Ou @Required
, ou @Index
pour toutes vos classes du modèle actuel
@Index
@AutoMigration.MigratedField(fieldAttributes = {FieldAttribute.INDEXED})
private String name;
@Required
@AutoMigration.MigratedField(fieldAttributes = {FieldAttribute.REQUIRED})
private String ownerName;
private Cat cat;
@AutoMigration.MigratedLink(linkType = Cat.class)
private RealmList<Cat> manyCats;
Ensuite, vous pouvez faire le code suivant
if (oldVersion == 0) {
//update from v0.10.5/update from v0.13.0
// if (schema.get("User").hasField("blocks") || !schema.get("Contact").hasField("pinned")) {
// realm.deleteAll();
// return;
//}
//addField(schema, "Message", "messageDuration", int.class);
//addField(schema, "Message", "starred", boolean.class);
//oldVersion++;
AutoMigration autoMigration = new AutoMigration();
autoMigration.migrate(realm, oldVersion, newVersion);
return; // <-- !! all fields of current model class version will be added !!
}
if(oldVersion == 1) {
// manual migration
}
S'il vous plaît essayer avec une base de données réelle cependant, le code est légèrement expérimentale.
Autre option serait d'ouvrir le royaume avec un royaume dynamique d'abord, vérifiez son schéma, si elle est trop vieux puis supprimez le royaume, et ouvrez-le avec la migration spécifiée par la suite.
DynamicRealm dynRealm = DynamicRealm.getInstance(config);
if(/* check schema like in migration*/) {
dynRealm.close();
Realm.delete(config);
}
if(!dynRealm.isClosed()) {
dynRealm.close();
}
Realm realm = Realm.getInstance(config);
Merci. J'utilise la deuxième option^_ ^ –