大約一年前,當(dāng)我次使用Realm 的時(shí)候,給我的印象非常不錯(cuò)。我需要保持一些用戶數(shù)據(jù)在手機(jī)本地,但是SharedPreferences用起來(lái)有點(diǎn)復(fù)雜了。Realm允許我用快速干凈的代碼里完成這件事情。它完全不需要像SQLite那樣自己手動(dòng)寫額外的代碼。
我接下來(lái)的一個(gè)項(xiàng)目在缺少網(wǎng)絡(luò)連接的時(shí)候需要一個(gè)比較復(fù)雜的離線模式。從網(wǎng)絡(luò)抓取的數(shù)據(jù)必須保存在手機(jī)本地。我決定完全使用Realm 并觀察它隨項(xiàng)目增大是如何擴(kuò)大的。
而我很快發(fā)現(xiàn)Realm讓數(shù)據(jù)模型的工作成了一種負(fù)擔(dān)。它有幾個(gè)限制以至于我必須在整個(gè)代碼基礎(chǔ)上做處理。結(jié)果我嘗試在Realm之上抽象出另外一層來(lái)減輕這種限制。
為了說(shuō)明這些限制,讓我們從簡(jiǎn)單的Person對(duì)象開(kāi)始:
|
1
2
3
4
5
6
7
8
9
10
|
public class Person extends RealmObject {
private String name;
private int age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
|
注意我們必須直接繼承自RealmObject。這阻礙我們利用數(shù)據(jù)模型中的任意類型的繼承。
并且我們還不能定義除setters 和 getters之外的實(shí)例方法。如果你想重寫equals 或者toString`那么你就別想了。這樣導(dǎo)致的另外一個(gè)后果就是我們只能局限于使用標(biāo)記接口模式(marker interfaces) (注解也是可以的 )。
不僅僅被限制于setters 和 getters,實(shí)際上我們還必須提供它們。因此我們的數(shù)據(jù)對(duì)象是不可變的!另外,setters 和 getters方法只是為Realm替換自己實(shí)現(xiàn)的代理方法。它不能操作數(shù)據(jù),跑出異常,或者打印日志。
雖然我們可以提供一個(gè)非默認(rèn)的構(gòu)造函數(shù),但是我們必須保證存在一個(gè)空的構(gòu)造函數(shù)。如果你想用一個(gè)builder 或者工廠方法來(lái)作為實(shí)例化的途徑,那么這種限制就成了一個(gè)問(wèn)題。稍后我們將看看如何用Realm創(chuàng)建對(duì)象。
在我們能持有的field類型方面,也有一些限制。所有的基本數(shù)據(jù)類型以及它們的封裝類型都能支持,包括String, Date, 和byte[]`。但是對(duì)于其它類型,為了被持久化,必須繼承自RealmObject。Lists可以用RealmList來(lái)支持。
但是也僅此而已。如果我們想使用枚舉而不是int,是沒(méi)有辦法的(找到一個(gè)使用@IntDef的理由了)。我們還不能使用集合類型,比如Set和Map。
為了創(chuàng)建一個(gè)Person類的實(shí)例,我們必須做如下事情:
|
1
2
3
4
5
6
|
Realm realm = Realm.getInstance(context);
realm.beginTransaction();
Person person = realm.createObject(Person.class);
person.setName("John");
person.setAge(25);
realm.commitTransaction();
|
你會(huì)注意到我們必須包裹一下Person 對(duì)象,同時(shí)任何對(duì)它的修改都在一個(gè)transaction 中。如果我們能在transaction 之外做這件事情并在我們準(zhǔn)備好的時(shí)候持久化它,就要靈活得多。而現(xiàn)在我們?cè)谙胍獎(jiǎng)?chuàng)建或者更新我們對(duì)象的任何時(shí)候都要卡在寫額外的Realm 代碼上面。
之前我提過(guò)我們可以定義一個(gè)非默認(rèn)的構(gòu)造函數(shù)。比如,對(duì)于Person我們可能有一個(gè)帶有name 和 age的構(gòu)造函數(shù):
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class Person extends RealmObject {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
// required empty public constructor
}
// setters and getters
...
}
|
我們不再需要直接調(diào)用setters :
|
1
2
3
4
|
Person person = new Person("John", 25);
realm.beginTransaction();
Person realmPerson = realm.copyToRealm(person);
realm.commitTransaction();
|
這讓我們省去了一些寫額外代碼的時(shí)間,但是仍然受限于transaction。
為了避免在基礎(chǔ)代碼中處理這些限制,我為數(shù)據(jù)對(duì)象定義了兩套類: POJOs (普通對(duì)象)和Realm 對(duì)象。然后我們創(chuàng)建了一個(gè)能在兩者之間映射的abstraction 。
這是可行的,但是有兩個(gè)主要的問(wèn)題。個(gè)是當(dāng)你持有許多不同類型的對(duì)象時(shí),你需要許多代碼來(lái)映射這些類。管理這些是很痛苦的而且這也很容易產(chǎn)生bug。 對(duì)象映射的概念以及它存在的問(wèn)題都不是什么新東西了。
第二個(gè)就是我覺(jué)得這有違初使用Realm的目的。能在持久層直接使用對(duì)象是它的主要好處。如果我們?yōu)榱耸褂肞OJO而必須在Realm 之上創(chuàng)建抽象,那么它相比SQLite或者像 DBFlow一樣的ORM的優(yōu)勢(shì)在哪里呢?
值得一提的是Realm 的維護(hù)者已經(jīng) 意識(shí)到了這些限制 ,而且在一定程度上,許多問(wèn)題都可能被解決(見(jiàn)這里 和 這里))。Realm也的確具有一些其它的優(yōu)勢(shì),比如性能以及在iOS和安卓之間共享數(shù)據(jù)的能力。
本站文章版權(quán)歸原作者及原出處所有 。內(nèi)容為作者個(gè)人觀點(diǎn), 并不代表本站贊同其觀點(diǎn)和對(duì)其真實(shí)性負(fù)責(zé),本站只提供參考并不構(gòu)成任何投資及應(yīng)用建議。本站是一個(gè)個(gè)人學(xué)習(xí)交流的平臺(tái),網(wǎng)站上部分文章為轉(zhuǎn)載,并不用于任何商業(yè)目的,我們已經(jīng)盡可能的對(duì)作者和來(lái)源進(jìn)行了通告,但是能力有限或疏忽,造成漏登,請(qǐng)及時(shí)聯(lián)系我們,我們將根據(jù)著作權(quán)人的要求,立即更正或者刪除有關(guān)內(nèi)容。本站擁有對(duì)此聲明的最終解釋權(quán)。