Realm 作為新一代移動端數據庫,其 Java 版本為 Android 開發帶來了極致的性能與開發體驗。本文涵蓋 Gradle 集成、模型定義、關聯查詢、異步操作、數據遷移、加密等完整技術棧,並對比 Swift 版本差異,助力 Android 開發者快速落地。
// Project build.gradle
buildscript {
repositories { jcenter() }
dependencies {
classpath "io.realm:realm-gradle-plugin:0.88.3"
}
}
// Module build.gradle
apply plugin: 'realm-android'
-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.**
boolean、byte、short、int、long、float、double、String、Date、byte[]、RealmObject、RealmList
使用 @Required 標註非空字段,適用於:Boolean、Byte、Short、Integer、Long、Float、Double、String、byte[]、Date。基本類型與 RealmList 隱含非空。注意: 必須提供 getter/setter,否則無法正確讀寫。
@Ignore
private String tempField;
與 Swift 版本一致,物件修改即時反映到所有引用:
realm.beginTransaction();
Dog myDog = realm.createObject(Dog.class);
myDog.setName("Fido");
myDog.setAge(1);
realm.commitTransaction();
Dog myPuppy = realm.where(Dog.class).equalTo("age", 1).findFirst();
realm.beginTransaction();
myPuppy.setAge(2);
realm.commitTransaction();
myDog.getAge(); // 返回 2
@Index
private String name;
@PrimaryKey
private long id; // 隱含 @Index + @Required
支援主鍵類型:String、short、int、long。關鍵提示: 使用 copyToRealmOrUpdate() 替代 createObject() 避免主鍵衝突。
public class Contact extends RealmObject {
private Email email; // 設為 null 斷開關聯,不刪除 Email 實例
}
public class Contact extends RealmObject {
private RealmList<Email> emails;
}
// 查找擁有棕色名為 Fluffy 狗狗的用戶
RealmResults<User> r1 = realm.where(User.class)
.equalTo("dogs.name", "Fluffy")
.equalTo("dogs.color", "Brown")
.findAll();
// 分步查詢(結果不同)
RealmResults<User> r2 = realm.where(User.class)
.equalTo("dogs.name", "Fluffy")
.findAll()
.where()
.equalTo("dogs.color", "Brown")
.findAll();
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
// 增刪改操作
realm.commitTransaction();
// 可隨時回滾
realm.beginTransaction();
User user = realm.createObject(User.class);
realm.cancelTransaction();
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");
}
});
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
User user = bgRealm.createObject(User.class);
user.setName("John");
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() { /* 成功回調 */ }
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) { /* 失敗回調 */ }
});
// 可取消的異步任務
RealmAsyncTask transaction = realm.executeTransactionAsync(...);
public void onStop() {
if (transaction != null && !transaction.isCancelled()) {
transaction.cancel();
}
}
採用 Fluent Interface 流式 API,支援惰性加載、引用語義、自動更新。
RealmResults<User> result = realm.where(User.class)
.equalTo("name", "John")
.or()
.equalTo("name", "Peter")
.findAll();
// 空結果 size() == 0,非 null
between()、greaterThan()、lessThan()、greaterThanOrEqualTo()、lessThanOrEqualTo()equalTo()、notEqualTo()contains()、beginsWith()、endsWith()beginGroup()、endGroup()、or()、not()result.sort("age");
result.sort("age", Sort.DESCENDING);
long sum = results.sum("age").longValue();
long min = results.min("age").longValue();
double avg = results.average("age");
// 鏈式查詢
RealmResults<User> teens = realm.where(User.class).between("age", 13, 20).findAll();
User firstJohn = teens.where().equalTo("name", "John").findFirst();
// 異步查詢(需 Looper 線程)
RealmResults<User> result = realm.where(User.class).findAllAsync();
result.addChangeListener(new RealmChangeListener() {
@Override
public void onChange() { /* UI 更新 */ }
});
// 強制同步加載(阻塞當前線程)
result.load();
RealmConfiguration config = new RealmConfiguration.Builder(context)
.name("myrealm.realm")
.encryptionKey(getKey())
.schemaVersion(42)
.setModules(new MySchemaModule())
.migration(new MyMigration())
.build();
Realm.setDefaultConfiguration(config);
// 一般線程
try {
Realm realm = Realm.getDefaultInstance();
// 使用 realm
} finally {
if (realm != null) realm.close();
}
// Looper 線程
public class MyThread extends Thread {
private Realm realm;
public void run() {
Looper.prepare();
try {
realm = Realm.getDefaultInstance();
Looper.loop();
} finally {
if (realm != null) realm.close();
}
}
}
// API 19+ try-with-resources
try (Realm realm = Realm.getDefaultInstance()) {
// 自動關閉
}
核心原則與 Swift 版本一致:不可跨線程傳遞 Realm 實例,各線程持有獨立快照。
// UI 線程
@Override
public void onActivityCreated(Bundle savedInstanceState) {
realm = Realm.getDefaultInstance();
RealmResults<Customer> customers = realm.where(Customer.class).findAllAsync();
customers.addChangeListener(new RealmChangeListener() {
@Override
public void onChange() {
listAdapter.notifyDataSetChanged(); // 自動更新 UI
}
});
}
// 後台服務線程
public class PollingService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Realm realm = Realm.getDefaultInstance();
String json = customerApi.getCustomers();
realm.beginTransaction();
realm.createObjectFromJson(Customer.class, json);
realm.commitTransaction(); // UI 線程自動收到變更通知
return START_STICKY;
}
}
支援類白名單、庫模組隔離、多模組合併:
// 庫模組(library = true 防止生成默認模組)
@RealmModule(library = true, allClasses = true)
public class MyLibraryModule {}
// App 整合庫模組
RealmConfiguration config = new RealmConfiguration.Builder(context)
.name("app.realm")
.setModules(Realm.getDefaultModule(), new MyLibraryModule())
.build();
支援 String、JSONObject、JSONArray、InputStream 四種來源:
realm.beginTransaction();
realm.createObjectFromJson(City.class, "{ city: \"Copenhagen\", id: 1 }");
realm.createObjectFromJson(City.class, jsonObject);
realm.createObjectFromJson(City.class, jsonArray);
realm.createAllFromJson(City.class, inputStream);
realm.commitTransaction();
Null 處理規則: 非 @Required 字段設為 null;@Required 字段拋異常;JSON 缺失字段保持原值不變。
僅在 Looper 線程回調,非 Looper 線程需手動 Realm.refresh()。支援 Realm、RealmObject、RealmResults 級別監聽,Listener 必須強引用持有:
// 類級別監聽(含關聯變更)
Person person = realm.where(Person.class).findFirst();
person.addChangeListener(new RealmChangeListener() {
@Override
public void onChange() {
// 關聯 Dog 更新也會觸發
}
});
採用線性遷移模式,必須處理跨版本升級:
RealmMigration migration = new RealmMigration() {
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
// v0 → v1: 合併 firstName + lastName → fullName
if (oldVersion == 0) {
RealmObjectSchema personSchema = schema.get("Person");
personSchema.addField("fullName", String.class, FieldAttribute.REQUIRED)
.transform(obj -> obj.set("fullName", obj.getString("firstName") + " " + obj.getString("lastName")))
.removeField("firstName")
.removeField("lastName");
oldVersion++;
}
// v1 → v2: 新增 Pet 類與關聯
if (oldVersion == 1) {
RealmObjectSchema petSchema = schema.create("Pet")
.addField("name", String.class, FieldAttribute.REQUIRED)
.addField("type", String.class, FieldAttribute.REQUIRED);
schema.get("Person")
.addRealmListField("pets", petSchema)
.transform(obj -> { /* 初始化數據 */ });
oldVersion++;
}
// v2 → v3: 字段類型變更與可空性調整
if (oldVersion == 2) {
schema.get("Person").setNullable("fullName", true);
schema.get("Pet")
.addField("type_tmp", int.class)
.transform(obj -> { /* 字符串轉枚舉 */ })
.removeField("type")
.renameField("type_tmp", "type");
}
}
};
AES-256 加密(64 字節密鑰),性能損耗 <10%。加密後無法用 Browser 查看,每次開啟需提供密鑰:
byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
RealmConfiguration config = new RealmConfiguration.Builder(context)
.encryptionKey(key)
.build();
Realm realm = Realm.getInstance(config);
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 1.x
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.setConverter(new GsonConverter(gson))
.build();
// Retrofit 2.x
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();