2017-10-11 158 views
1

境界平臺:手機的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++; 
    } 
} 

回答

1

如果您使用以下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); 
      } 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; 
    } 
} 

然後你註釋你的字段當模型類有@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 
} 

請一個真正的數據庫,雖然嘗試,代碼略有實驗。


其他選項是首先打開一個動態的境界境界,檢查它的架構,如果它太舊,然後刪除該領域,並與特定遷移打開它之後。

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); 
+1

謝謝。我使用第二個選項^ _ ^ –