领域平台:移动Android
领域版本: 3.3.2
加密:是
我有一个制作应用。我在我的应用程序v0.3.0上迁移到v0.14.0时使用自动删除领域。由于某种原因,在v0.14.2我必须手动迁移。并且有几个用户因为从v0.14.0下的版本更新而出错,我只处理从v0.14.0到v0.14.2的迁移。如果我从开始处理版本,我感到困惑,因为我必须做很多迁移。所以我想在我的迁移中手动删除领域,如果用户从v0.14.0下的版本更新我的应用程序。怎么做?
我喜欢这样,但我仍然有RealmMigrationNeededException
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
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++;
}
if (oldVersion == 1) {
if (!schema.contains("PhoneBook")) {
schema.create("PhoneBook")
.addField("phone", String.class)
.addPrimaryKey("phone")
.addField("name", String.class)
.addField("image", String.class);
}
if (schema.contains("RealmString")) {
schema.remove("RealmString");
}
oldVersion++;
}
}
答案 0 :(得分:4)
如果您使用以下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*
然后使用以下代码
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);
}
}
// 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);
}
// it is now safe to remove classes if they were removed from the model.
for(String objectClassName : schemaClassNames) {
RealmObjectSchema objectSchema = schemaClassNameToObjectSchemaMap.get(objectClassName);
if(!modelClassNames.contains(objectClassName)) {
// the model class was not part of the schema, so we must remove the object schema.
realmSchema.remove(objectClassName);
}
}
}
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;
}
}
然后,您为所有当前模型类的模型类标注@PrimaryKey
,@Ignore
,@Required
或@Index
时注释字段
@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;
然后你可以做以下代码
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
}
请尝试使用真实的数据库,代码稍微实验。
其他选项是首先使用动态Realm打开Realm,检查其架构,如果它太旧则删除Realm,然后使用指定的迁移打开它。
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);