Realm Java

Realm Java

当前这个翻译,主要是方便我自己查阅api,有很多地方写的比较晦涩或者没有翻译,敬请谅解

version 0.89.0

官方文档

Getting Started

Installation

工程的build.gradle中添加

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "io.realm:realm-gradle-plugin:0.88.3"
    }
}

引用module的build.gradle中添加

apply plugin: 'realm-android'

ProGuard

-keep class io.realm.annotations.RealmModule
-keep @io.realm.annotations.RealmModule class *
-keep class io.realm.internal.Keep
-keep @io.realm.internal.Keep class * { *; }
-dontwarn javax.**
-dontwarn io.realm.**

Models

Realm支持类型

  • boolean
  • byte
  • short
  • int
  • long
  • float
  • double
  • String
  • Date
  • byte[]
  • RealmObject
  • RealmList

Required fields and null values

必须属性和null值

  • 在某些情况下,存在null值是不合理的.
  • @Required可以声明Boolean, Byte, Short, Integer, Long, Float, Double, String, byte[], Date,不能为null.
  • 当其他类型使用@Required,会导致编译错误.
  • Fields with primitive types and the RealmList type are required implicitly.(必须通过setter和getter,否则不会获取到正确数据)
  • RealmObject不需要默认值

Ignoring properties

@Ignore来声明Realm忽略的属性

Auto-Updating Objects

和ios一样

顾名思义当一个数据的内容改变时,它会自动更新该数据的所有实例化对象

realm.beginTransaction();
Dog myDog = realm.createObject(Dog.class);
myDog.setName("Fido");
myDog.setAge(1);
realm.commitTransaction();  

Dog myPuppy = realm.where(Dog.class).equals("age", 1).findFirst();
realm.beginTransaction();
myPuppy.setAge(2);
realm.commitTransaction();

myDog.getAge(); // => 2

当数据变化,需要更新界面时,需要配合 [Realm notifications](#Realm notifications) 实现,后续会详细描述这功能

Indexed Properties

@Index 来修饰索引类型

索引支持类型

  • String
  • byte
  • short
  • int
  • long
  • boolean
  • Date
注意事项(IOS的,Android没有说)


Indexing a property will greatly speed up queries where the property is compared for equality (i.e. the = and IN operators), at the cost of slower insertions.


使用索引增加查询速度的代价是插入数据时速度会降低

Primary Keys

@PrimaryKey 用来修饰主键.@PrimaryKey可以修饰String,short,int,long.

@PrimaryKey只能修饰一个属性.

@PrimaryKey声明一个主键后,该属性会被隐性声明成@Index.

@PrimaryKey声明一个主键后,该属性会被隐性声明成@Required,不能为null.

声明主键之后,对象将被允许查询,更新速度更加高效,并且要求每个对象保持唯一性。
一旦带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。

当调用Realm.createObject(),它会返回一个有默认值的对象,当有主键时,主键会被设置默认值,这样可能会与已有对象冲突.所以最好使用copyToRealm()或者copyToRealmOrUpdate()替换

MyObject obj = new MyObject();
obj.setId(42);
obj.setName("Fish");
realm.beginTransaction();
// This will create a new one in Realm
// realm.copyToRealm(obj);
// This will update a existing one with the same id or create a new one instead
realm.copyToRealmOrUpdate(obj);
realm.commitTransaction();

Ignored Properties

重写 Object.ignoredProperties() 可以防止 Realm 存储数据模型的某个属性。Realm 将不会干涉这些属性的常规操作,它们将由成员变量(ivar)提供支持,并且您能够轻易重写它们的 setter 和 getter。

class Person: Object {
  dynamic var tmpID = 0
  var name: String { // read-only properties are automatically ignored
    return "\(firstName) \(lastName)"
  }
  dynamic var firstName = ""
  dynamic var lastName = ""

  override static func ignoredProperties() -> [String] {
    return ["tmpID"]
  }
}

Limitations

Realm不支持final,transient和volatile修饰

Relationships

public class Email extends RealmObject {
    private String address;
    private boolean active;
    // ... setters and getters left out
}

public class Contact extends RealmObject {
    private String name;
    private Email email;
    // ... setters and getters left out
}
一对一(To-One Relationships)
public class Contact extends RealmObject {
    private Email email;
    // Other fields…
}

如果将email设为null,会断开2个实例间的关系,但是email实例没有被删除

一对多(To-Many Relationships)
class Person: Object {
    public class Contact extends RealmObject {
    private RealmList<Email> emails;
    // Other fields…
}
}
public class Person extends RealmObject {
  private String id;
  private String name;
  private RealmList<Dog> dogs;
  // getters and setters
}

public class Dog extends RealmObject {
  private String id;
  private String name;
  private String color;
  // getters and setters
}

img

// users => [U1,U2]
RealmResults<User> users = realm.where(User.class)
                                .equalTo("dogs.color", "Brown")
                                .findAll();
// r1 => [U1,U2]
RealmResults<User> r1 = realm.where(User.class)
                             .equalTo("dogs.name", "Fluffy")
                             .equalTo("dogs.color", "Brown")
                             .findAll();

// r2 => [U2]
RealmResults<User> r2 = realm.where(User.class)
                             .equalTo("dogs.name", "Fluffy")
                             .findAll()
                             .where()
                             .equalTo("dogs.color", "Brown")
                             .findAll();
                             .where()
                             .equalTo("dogs.color", "Yellow")
                             .findAll();

Writes

所有写入操作(添加,修改,删除)都必须依托一个write transaction.

由于write transaction会占用一定的资源,所以尽量精简write transaction的个数.当队列写入时,只需要一个就write transaction

在UI线程和后台线程同时开始写事务时,会导致ARN

写事务都是线程安全的.

// Obtain a Realm instance
Realm realm = Realm.getDefaultInstance();

realm.beginTransaction();

//... add or update objects here ...

realm.commitTransaction();

当开始写操作后,可以随时取消

realm.beginTransaction();
User user = realm.createObject(User.class);

//  ... 

realm.cancelTransaction();

Creating Objects

由于RealmObjects必须依赖在Realm,所以其创建方式如下:

realm.beginTransaction();
User user = realm.createObject(User.class); // Create a new object
user.setName("John");
user.setEmail("john@corporation.com");
realm.commitTransaction();

//========other

User user = new User("John");
user.setEmail("john@corporation.com");

// Copy the object to Realm. Any further changes must happen on realmUser
realm.beginTransaction();
User realmUser = realm.copyToRealm(user);  
realm.commitTransaction();

当使用realm.copyToRealm后,对原始数据修改,将不会被同步到Realm中.

Transaction blocks

会自动执行realm.beginTransaction(), realm.commitTransaction()

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        User user = realm.createObject(User.class);
        user.setName("John");
        user.setEmail("john@corporation.com");
    }
});

Asynchronous Transactions

异步事务,主要是用于大量数据写入

realm.executeTransactionAsync(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
                User user = bgRealm.createObject(User.class);
                user.setName("John");
                user.setEmail("john@corporation.com");
            }
        }, new Realm.Transaction.OnSuccess() {
            @Override
            public void onSuccess() {
                // Transaction was a success.    
            }
        }, new Realm.Transaction.OnError() {
            @Override
            public void onError(Throwable error) {
                // Transaction failed and was automatically canceled.
            }
        });
RealmAsyncTask transaction = realm.executeTransactionAsync(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
                User user = bgRealm.createObject(User.class);
                user.setName("John");
                user.setEmail("john@corporation.com");
            }
        }, null);

public void onStop () {
    if (transaction != null && !transaction.isCancelled()) {
        transaction.cancel();
    }
}

Queries

通过查询操作,Realm 将会返回包含 Object 集合的Results实例。Results 的表现和 List 十分相似。

所有的查询(包括查询和属性访问)在 Realm 中都是延迟加载的,只有当属性被访问时,才能够读取相应的数据。也就是说当没有使用数据前,进行多次排序或者过滤都是不需要额外cpu时间的

查询结构不是Copy对象,而是引用对象.所以在Write操作中修改查询数据,是直接修改数据库中的数据.

Realm使用Fluent interface,也就是流接口

基本查询语句

// Build the query looking at all users:
RealmQuery<User> query = realm.where(User.class);

// Add query conditions:
query.equalTo("name", "John");
query.or().equalTo("name", "Peter");

// Execute the query:
RealmResults<User> result1 = query.findAll();

// Or alternatively do the same all at once (the "Fluent interface"):
RealmResults<User> result2 = realm.where(User.class)
                                  .equalTo("name", "John")
                                  .or()
                                  .equalTo("name", "Peter")
                                  .findAll();

当查询内容为空时,RealmResults不会为null,它的size==0

Conditions

  • between, greaterThan(), lessThan(), greaterThanOrEqualTo() & lessThanOrEqualTo()
  • equalTo() & notEqualTo()
  • contains(), beginsWith() & endsWith()

Logical Operators

主要是or()和not()

not(),用来否定花括号中的条件

流接口中支持”花括号”,beginGroup() => ‘{’ , endGroup() => ‘}’

RealmResults<User> r = realm.where(User.class)
                            .greaterThan("age", 10)  //implicit AND
                            .beginGroup()
                                .equalTo("name", "Peter")
                                .or()
                                .contains("name", "Jo")
                            .endGroup()
                            .findAll();

Sorting

RealmResults<User> result = realm.where(User.class).findAll();
result.sort("age"); // Sort ascending
result.sort("age", Sort.DESCENDING);

Chaining Queries

RealmResults<User> teenagers = realm.where(User.class).between("age", 13, 20).findAll();
User firstJohn = teenagers.where().equalTo("name", "John").findFirst();

RealmResults<User> teensWithPups = realm.where(User.class).between("age", 13, 20).equalTo("dogs.age", 1).findAll();

Auto-Updating Results

结果会自动更新

RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
puppies.size(); // => 0

realm.beginTransaction();
Dog dog = realm.createObject(Dog.class);
dog.setName("Fido");
dog.setAge(1);
realm.commitTransaction();  

puppies.size(); // => 1

Retrieving objects by type

realm.allObjects()

Aggregation

RealmResults<User> results = realm.where(User.class).findAll();
long   sum     = results.sum("age").longValue();
long   min     = results.min("age").longValue();
long   max     = results.max("age").longValue();
double average = results.average("age");

long   matches = results.size();

Iterations

RealmResults<User> results = realm.where(User.class).findAll();
for (User u : results) {
    // ... do something with the object ...
}

RealmResults<User> results = realm.where(User.class).findAll();
for (int i = 0; i < results.size(); i++) {
    User u = results.get(i);
    // ... do something with the object ...
}

// Find all dogs older than or equal to 2 and change their age to 1
RealmResults results = realm.where(Dog.class).greaterThanOrEqualTo("age", 2).findAll();
realm.beginTransaction();
for (int i = results.size() -1; i >=0; i--) {
   results.get(i).setAge(1);
}
realm.commitTransaction();

Deletion

// obtain the results of a query
RealmResults<Dog> results = realm.where(Dog.class).findAll();

// All changes to data must happen in a transaction
realm.beginTransaction();

// remove single match
results.remove(0);
results.removeLast();

// remove a single object
Dog dog = results.get(5);
dog.removeFromRealm();

// Delete all matches
results.clear();

realm.commitTransaction()

Asynchronous Queries

private RealmChangeListener callback = new RealmChangeListener() {
    @Override
    public void onChange() { // called once the query complete and on every update
    // use the result
    }
};

public void onStart() {
    RealmResults<User> result = realm.where(User.class).findAllAsync();
    result.addChangeListener(callback);
}
public void onStop () {
    result.removeChangeListener(callback); // remove a particular listener
    // or
    result.removeChangeListeners(); // remove all registered listeners
}
Check if the query has completed

当RealmResults有数据绑定后,result.isLoaded(),会一直返回true

RealmResults<User> result = realm.where(User.class).findAllAsync();
if (result.isLoaded()) {
  // Results are now available
}
Force load an asynchronous query

强制执行异步查询,会阻塞当前线程.

RealmResults<User> result = realm.where(User.class).findAllAsync();
result.load() // be careful, this will block the current thread until it returns
Non-Looper threads

异步查询只能使用在有Looper的线程上,否则会抛出异常.

Realms

Realm Configuration

Realm.setDefaultConfiguration()直接设置默认配置

Realm默认会将文件存储在Context.getFilesDir(),文件路径是无法修改的

// The RealmConfiguration is created using the builder pattern.
// The realm file will be located in Context.getFilesDir() with name "myrealm.realm"
RealmConfiguration config = new RealmConfiguration.Builder(context)
  .name("myrealm.realm")
  .encryptionKey(getKey())
  .schemaVersion(42)
  .setModules(new MySchemaModule())
  .migration(new MyMigration())
  .build();
// Use the config
Realm realm = Realm.getInstance(config);


RealmConfiguration myConfig = new RealmConfiguration.Builder(context)
  .name("myrealm.realm").
  .schemaVersion(2)
  .setModules(new MyCustomSchema())
  .build();

RealmConfiguration otherConfig = new RealmConfiguration.Builder(context)
  .name("otherrealm.realm")
  .schemaVersion(5)
  .setModules(new MyOtherSchema())
  .build();

Realm myRealm = Realm.getInstance(myConfig);
Realm otherRealm = Realm.getInstance(otherConfig);

The Default RealmConfiguration

public class MyApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    // The realm file will be located in Context.getFilesDir() with name "default.realm"
    RealmConfiguration config = new RealmConfiguration.Builder(this).build();
    Realm.setDefaultConfiguration(config);
  }
}

public class MyActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Realm realm = Realm.getDefaultInstance();
    // ... Do something ...
    realm.close();
  }
}

In-Memory Realms

内存中的Realms,没有保存在磁盘上.

优点:可以快速的访问数据,而不需要考虑数据持久化的性能开销.内存Realms只会在temp路径里存放几个文件,用来进行线程间数据同步,不会将Realms中任何数据写入磁盘中

Dynamic Realms

不需要建立基于RealmObject类,只需要通过字符串来操作.

主要优势是灵活

RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
DynamicRealm realm = DynamicRealm.getInstance(realmConfig);

// In a DynamicRealm all objects are DynamicRealmObjects
DynamicRealmObject person = realm.createObject("Person");

// All fields are accesssed using strings
String name = person.getString("name");
int age = person.getInt("age");

// An underlying schema still exists, so accessing a field that does not exist
// will throw an exception
person.getString("I don't exist");

// Queries stil work normally
RealmResults<DynamicRealmObject> persons = realm.where("Person")
    .equalTo("name", "John")
    .findAll();

Closing Realm instances

protected Void doInBackground(Void... params) {
    Realm realm = null;
    try {
        realm = Realm.getDefaultInstance();
        // ... Use the Realm instance
    } finally {
        if (realm != null) {
            realm.close();
        }
    }

    return null;
}

在有Looper的线程中,应该如下使用:

public class MyThread extends Thread {

    private Realm realm;

    public void run() {
        Looper.prepare();
        try {
            realm = Realm.getDefaultInstance();

            //... Setup the handlers using the Realm instance
            Lopper.loop();
        } finally {
            if (realm != null) {
                realm.close();
            }
        }
    }
}

在minSdkVersion >= 19时,可以如下使用:

try (Realm realm = Realm.getDefaultInstance()) {
    // No need to close the Realm instance manually
}

Auto-Refresh

在有Looper的线程上(包括UI线程)会自动更新.

可以使用isAutoRefresh()来知晓是否当前Realm是否支持自动刷新

Threading

这一章主要是讲多线程开发,大量写入事务最好是放在其他线程中,以防止UI线程被阻塞

只在一个线程中处理所有事情,不需要担心并发和多线程.(然并卵的话)

Realm在多线程处理上不需要使用线程锁,只需要注意写入操作需要在Write事件中.

Realm Threading Example

// in a Fragment or Activity, etc
@Override
public void onActivityCreated(Bundle savedInstanceState) {
    // ... boilerplate omitted for brevity
    realm = Realm.getDefaultInstance();
    // get all the customers
    RealmResults<Customer> customers = realm.where(Customer.class).findAllAsync();
    // ... build a list adapter and set it to the ListView/RecyclerView/etc

    // set up a Realm change listener
    changeListener = new RealmChangeListener() {
        @Override
        public void onChange() {
            // This is called anytime the Realm database changes on any thread.
            // Please note, change listeners only work on Looper threads.
            // For non-looper threads, you manually have to use Realm.refresh() instead.
            listAdapter.notifyDataSetChanged(); // Update the UI
        }
    };
    // Tell Realm to notify our listener when the customers results
    // have changed (items added, removed, updated, anything of the sort).
    customers.addChangeListener(changeListener);
}

// In a background service, in another thread
public class PollingService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Realm realm = Realm.getDefaultInstance();
        // go do some network calls/etc and get some data and stuff it into a 'json' var
        String json = customerApi.getCustomers();
        realm.beginTransaction();
        realm.createObjectFromJson(Customer.class, json); // Save a bunch of new Customer objects
        realm.commitTransaction();
        // At this point, the data in the UI thread is already up to date.
        // ...
    }
    // ...
}

Using a Realm across Threads

在UI线程或者其他添加Runloop的线程上,Realm都会自动更新其他线程Runloop的操作结果.(这里是说其他线程有更新,UI线程或Runloop线程都不会更新数据)

在其他类型的线程上操作,都是基于Snapshots.

UI线程或者其他添加Runloop的线程上,数据都会自动刷新,除非将Realm.autorefresh设置为NO

其他类型的线程,都是以最后一次修改成功的Realm为snapshot,除非是手动refresh

Realm, RealmObject or RealmResults不能进行多线程传递.

最好使用asynchronous query和asynchronous transaction

```


##Schemas
可以让Realm只包含特定类型.

// Create the module
@RealmModule(classes = { Person.class, Dog.class })
public class MyModule {
}

// Set the module in the RealmConfiguration to allow only classes defined by the module.
RealmConfiguration config = new RealmConfiguration.Builder(context)
.setModules(new MyModule())
.build();

// It is possible to combine multiple modules to one schema.
RealmConfiguration config = new RealmConfiguration.Builder(context)
.setModules(new MyModule(), new MyOtherModule())
.build();


###Sharing schemas

```基于Library开发时,需要注意Realm必须expose并且Realm必须设置对应的schema.
如果不设置schema,当你使用它们时,会抛出异常.




<div class="se-preview-section-delimiter"></div>
// Library must create a module and set library = true. This will prevent the default
// module from being created.
// allClasses = true can be used instead of listing all classes in the library.
@RealmModule(library = true, allClasses = true)
public class MyLibraryModule {
}

// Library projects are therefore required to explicitly set their own module.
RealmConfiguration libraryConfig = new RealmConfiguration.Builder(context)
  .name("library.realm")
  .setModules(new MyLibraryModule())
  .build();

// Apps can add the library RealmModule to their own schema.
RealmConfiguration config = new RealmConfiguration.Builder(context)
  .name("app.realm")
  .setModules(Realm.getDefaultModule(), new MyLibraryModule())
  .build();




<div class="se-preview-section-delimiter"></div>

JSON

支持导入类型String,JSONObject,JSONArray和InputStream.

// A RealmObject that represents a city
public class City extends RealmObject {
    private String city;
    private int id;
    // getters and setters left out ...
}

// Insert from a string
realm.beginTransaction();
realm.createObjectFromJson(City.class, "{ city: \"Copenhagen\", id: 1 }");
realm.commitTransaction();

// Insert from a JSONObject
realm.beginTransaction();
realm.createObjectFromJson(City.class, jsonObject);
realm.commitTransaction();

// Insert from a JSONArray
realm.beginTransaction();
realm.createObjectFromJson(City.class, jsonArray);
realm.commitTransaction();

// Insert multiple items using a InputStream
InputStream is = new FileInputStream(new File("path_to_file"));
realm.beginTransaction();
try {
    realm.createAllFromJson(City.class, is);
    realm.commitTransaction();
} catch (IOException e) {
    realm.cancelTransaction();
}




<div class="se-preview-section-delimiter"></div>
注意事项
  • 使用JSON创建对象属性为null值
    • 没有用@Required修饰的会被赋值null
    • 用@Required的会抛出异常
  • 使用JSON更新对象属性为null值
    • 没有用@Required修饰的会被赋值null
    • 用@Required的会抛出异常
  • JSON没有属性对应的字段
    • Leave the value unchanged for both required and not-required fields.(意思好像是,不会改变其原值,没有具体说是创建还是更新)

Notifications

修改Listeners只会在有Looper的线程(包含UI线程)中回调,其他线程只能使用Realm.refresh()来刷新

在多线程中修改数据,有Looper的线程(包含UI线程)都会收到Listeners的回调

public class MyActivity extends Activity {
    private Realm realm;
    // A reference to RealmChangeListener needs to be held to avoid being
    // removed by the garbage collector.
    private RealmChangeListener realmListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      realm = Realm.getDefaultInstance();
      reamlListener = new RealmChangeListener() {
        @Override
        public void onChange() {
            // ... do something with the updates (UI, etc.) ...
        }};
      realm.addChangeListener(realmListener);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Remove the listener.
        realm.removeChangeListener(realmListener);
        // Close the realm instance.
        realm.close();
    }
}




<div class="se-preview-section-delimiter"></div>

关闭所有Listeners

realm.removeAllChangeListeners();




<div class="se-preview-section-delimiter"></div>

RealmObject和RealmResults都可以添加Listeners

public class MyActivity extends Activity {
    private Realm realm;
    private RealmChangeListener puppiesListener;
    private RealmChangeListener dogListener;

    private RealmResults<Dog> puppies;
    private Dog dog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      realm = Realm.getDefaultInstance();
      puppiesListener = new RealmChangeListener() {
        @Override
        public void onChange() {
            // ... do something with the updated puppies instance
        }};

      // Find all the puppies
      puppies = realm.where(Dog.class).lessThanOrEqualTo("age", 2).findAll();
      puppies.addChangeListener(puppiesListener);

      dogListener = new RealmChangeListener() {
        @Override
        public void onChange() {
            // ... do something with the updated Dog instance
        }};

      dog = realm.where(Dog.class).equals("name", "Fido").findFirst();
      dog.addChangeListener(dogListener);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Remove the listeners
        puppies.removeChangeListener(puppiesListener);
        dog.removeChangeListener(dogListener);
        // Close the realm instance.
        realm.close();
    }
}




<div class="se-preview-section-delimiter"></div>

给类添加Listeners

Person person = realm.where(Person.class).findFirst();
person.getDogs(); // => 2 - Assume there are 2 dogs in the list
person.addChangeListener(new RealmChangeListener() {
                             @Override
                             public void onChange() {
                                 // React to the change in the Person instance.
                                 // This will also get called when any referenced dogs are updated.
                             }
                         });
Dog dog = person.getDogs().get(0);
realm.beginTransaction();
dog.setAge(5);
realm.commitTransaction();
// Person change listener is called on the next iteration of the run loop because
// a referenced dog object changed.




<div class="se-preview-section-delimiter"></div>

Migrations

数据迁移,版本迭代时,数据库常用

为什么要进行数据库迁移(借用Swift)

class Person: Object {
    dynamic var firstName = ""
    dynamic var lastName = ""
    dynamic var age = 0
}




<div class="se-preview-section-delimiter"></div>

在某个版本更新中,变成了下边这样

class Person: Object {
    dynamic var fullName = ""
    dynamic var age = 0
}




<div class="se-preview-section-delimiter"></div>

那么就需要用到数据迁移了.

Linear Migrations

需要考虑跨版本的数据库迁移,例如v0直接升级到v3版本,而不是只考虑v2升级到v3.

// Example migration adding a new class
RealmMigration migration = new RealmMigration() {
  @Override
  public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {

     // During a migration, a DynamicRealm is exposed. A DynamicRealm is an untyped variant of a normal Realm, but
        // with the same object creation and query capabilities.
        // A DynamicRealm uses Strings instead of Class references because the Classes might not even exist or have been
        // renamed.

        // Access the Realm schema in order to create, modify or delete classes and their fields.
        RealmSchema schema = realm.getSchema();

        /************************************************
            // Version 0
            class Person
                @Required
                String firstName;
                @Required
                String lastName;
                int    age;

            // Version 1
            class Person
                @Required
                String fullName;            // combine firstName and lastName into single field.
                int age;
        ************************************************/
        // Migrate from version 0 to version 1
        if (oldVersion == 0) {
            RealmObjectSchema personSchema = schema.get("Person");

            // Combine 'firstName' and 'lastName' in a new field called 'fullName'
            personSchema
                    .addField("fullName", String.class, FieldAttribute.REQUIRED)
                    .transform(new RealmObjectSchema.Function() {
                        @Override
                        public void apply(DynamicRealmObject obj) {
                            obj.set("fullName", obj.getString("firstName") + " " + obj.getString("lastName"));
                        }
                    })
                    .removeField("firstName")
                    .removeField("lastName");
            oldVersion++;
        }

        /************************************************
            // Version 2
                class Pet                   // add a new model class
                    @Required
                    String name;
                    @Required
                    String type;

                class Person
                    @Required
                    String fullName;
                    int age;
                    RealmList<Pet> pets;    // add an array property

         ************************************************/
        // Migrate from version 1 to version 2
        if (oldVersion == 1) {

            // Create a new class
            RealmObjectSchema petSchema = schema.create("Pet")
                    .addField("name", String.class, FieldAttribute.REQUIRED)
                    .addField("type", String.class, FieldAttribute.REQUIRED);

            // Add a new field to an old class and populate it with initial data
            schema.get("Person")
                .addRealmListField("pets", petSchema)
                .transform(new RealmObjectSchema.Function() {
                    @Override
                    public void apply(DynamicRealmObject obj) {
                        if (obj.getString("fullName").equals("JP McDonald")) {
                            DynamicRealmObject pet = realm.createObject("Pet");
                            pet.setString("name", "Jimbo");
                            pet.setString("type", "dog");
                            obj.getList("pets").add(pet);
                        }
                    }
                });
            oldVersion++;
        }

        /************************************************
            // Version 3
                class Pet
                    @Required
                    String name;
                    int type;               // type becomes int

                class Person
                    String fullName;        // fullName is nullable now
                    RealmList<Pet> pets;    // age and pets re-ordered (no action needed)
                    int age;
         ************************************************/
        // Migrate from version 2 to version 3
        if (oldVersion == 2) {
            RealmObjectSchema personSchema = schema.get("Person");
            personSchema.setNullable("fullName", true); // fullName is nullable now.

            // Change type from String to int
            schema.get("Pet")
                .addField("type_tmp", int.class)
                .transform(new RealmObjectSchema.Function() {
                    @Override
                    public void apply(DynamicRealmObject obj) {
                        String oldType = obj.getString("type");
                        if (oldType.equals("dog")) {
                            obj.setLong("type_tmp", 1);
                        } else if (oldType.equals("cat")) {
                            obj.setInt("type_tmp", 2);
                        } else if (oldType.equals("hamster")) {
                            obj.setInt("type_tmp", 3);
                        }
                    }
                })
                .removeField("type")
                .renameField("type_tmp", "type");
            oldVersion++;
        }
  }




<div class="se-preview-section-delimiter"></div>

Encryption

Realm的加密方式为:key为64字节,AES-256

加密过的 Realm 只会带来很少的额外资源占用(通常最多只会比平常慢10%)

注:如果数据库加密后,由于不知道加密方式,即使有原始key,也无法获取解密key,所以无法用Realm Browser查看.
注:如果数据库加密,每次获取Realm实例时,必须使用encryptionKey.

    byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
RealmConfiguration config = new RealmConfiguration.Builder(context)
  .encryptionKey(key)
  .build();

Realm realm = Realm.getInstance(config);




<div class="se-preview-section-delimiter"></div>

Other Libraries

GSON

GSON is a library created by Google for deserializing and serializing JSON. When using Realm with GSON 2.3.1 (latest version), you have to specify an ExclusionStrategy.

// Using the User class
public class User extends RealmObject {
    private String name;
    private String email;
    // getters and setters left out ...
}

Gson gson = new GsonBuilder()
        .setExclusionStrategies(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return f.getDeclaringClass().equals(RealmObject.class);
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return false;
            }
        })
        .create();

String json = "{ name : 'John', email : 'john@corporation.com' }";
User user = gson.fromJson(json, User.class);




<div class="se-preview-section-delimiter"></div>

Retrofit

Retrofit 1.*

uses GSON internally, it also needs a properly configured GsonConverter if you want to deserialize network JSON data to RealmObjects.

Gson gson = new GsonBuilder()
        .setExclusionStrategies(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return f.getDeclaringClass().equals(RealmObject.class);
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return false;
            }
        })
        .create();

// Configure Retrofit to use the proper GSON converter
RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint("https://api.github.com")
    .setConverter(new GsonConverter(gson))
    .build();

GitHubService service = restAdapter.create(GitHubService.class);




<div class="se-preview-section-delimiter"></div>
Retrofit 2.*

With Retrofit 2, GSON is no longer used by default, but can be used via its converter module.

dependencies {
    compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'
    compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3'




<div class="se-preview-section-delimiter"></div>
Gson gson = new GsonBuilder()
        .setExclusionStrategies(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return f.getDeclaringClass().equals(RealmObject.class);
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return false;
            }
        })
        .create();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com")
                .addConverterFactory(GsonConverterFactory.create(gson)
                .build();
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值