Realm學(xué)習(xí)總結(jié)

一临扮、入門篇

1.如何安裝Realm

第一步:在Project級別的 build.gradle 文件中添加依賴:

buildscript {
    ...
    dependencies {
        ...
        classpath "io.realm:realm-gradle-plugin:5.13.0"
    }
    ...
}

第二步:在app級別的build.gradle文件中添加下面這一行:

apply plugin: 'realm-android'

第三步: 同步( sync )項(xiàng)目配置,Android Studio運(yùn)行結(jié)束后即可完成安裝。

2.如何定義Realm對象

定義Realm對象的方法非常簡單,只需定義一個(gè)Model枕稀,然后繼承RealmObject即可。

//定義Dog類,繼承RealmObject
public class Dog extends RealmObject {
    private String name;
    private int age;
    
    //注意:不能定義構(gòu)造函數(shù)近忙,否則編譯不通過。
    //如若想定義有參構(gòu)造函數(shù)智润,則必須要有一個(gè)public修飾的無參構(gòu)造函數(shù)
    //public Dog(String name, int age) {
    //    this.name = name;
    //    this.age = age;
    //}
    
    //自動生成的標(biāo)準(zhǔn) getter\setter方法 
    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;
    }
}

3.如何初始化Realm數(shù)據(jù)庫

//Realm數(shù)據(jù)庫的默認(rèn)初始化方式:
Realm.init(context); //初始化數(shù)據(jù)庫
Realm realm = Realm.getDefaultInstance();//獲取默認(rèn)配置的實(shí)例

Realm數(shù)據(jù)庫在使用前需要被初始化及舍,因?yàn)樾枰?code>init(Context context)函數(shù)傳遞context,因此我們可以在Application中進(jìn)行初始化操作:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Realm.init(this);
    }
}

如果在Application類中初始化,那么我們還需對AndroidManifest.xml做如下修改:

<application
    android:name=".MyApplication"
    ...
</application>

4.如何使用Realm數(shù)據(jù)庫

//向realm數(shù)據(jù)庫中存儲Dog對象
realm.beginTransaction();
Dog dog = realm.createObject(Dog.class);
dog.setAge(2);
dog.setName("金毛");
realm.commitTransaction();

5.如何查看Realm數(shù)據(jù)庫

默認(rèn)Realm數(shù)據(jù)庫文件名為default.realm,我們可以使用官方推薦的Realm Studio軟件查看窟绷、編輯數(shù)據(jù)庫锯玛。

二、基礎(chǔ)篇

1.Realm Model

(a)屬性類型

上面簡單介紹過Realm Model的創(chuàng)建方式兼蜈,這里不再贅述攘残。Realm Model支持定義多種類型的字段,如:boolean为狸、byte歼郭、shortint辐棒、long病曾、doubleString漾根、Datebyte[]類型泰涂。其中,byte辐怕、short逼蒙、intlong都將會被應(yīng)設(shè)成long類型寄疏。同時(shí)是牢,Realm還支持ReamlObject派生的子類和ReamlObject數(shù)組顶考。
此外,Realm Model還支持以上幾種類型對應(yīng)的包裝類型,并且默認(rèn)值為null妖泄。

(b) 非空屬性

Realm Model通過注解@required來聲明屬性值是否非空驹沿。這里需要注意的是非空是針對默認(rèn)值為null的屬性而言,也就是說只有上述幾種基本類型的包裝類型才能被聲明為@required

public class Dog extends RealmObject {
    @Required //正確蹈胡。String類型的屬性默認(rèn)值為null渊季,因此可以聲明為required
    private String name;
    @Required //錯(cuò)誤。int類型的默認(rèn)值為0罚渐,聲明為required毫無意義
    private int age;
    ......
}

(c) 主鍵

Realm數(shù)據(jù)庫同樣能夠給數(shù)據(jù)表設(shè)置主鍵却汉,支持設(shè)置主鍵的類型包括:byteshort荷并、int合砂、longByte源织、Short翩伪、IntegerLong以及String谈息。設(shè)置主鍵的方式是在對應(yīng)的字段加注解@PrimaryKey,但是不支持聯(lián)合主鍵缘屹。值得注意的是,如果將String類型的屬性設(shè)置為了主鍵侠仇,那么將會隱示地為其添加@Index注解轻姿。

public class Dog extends RealmObject {

    @PrimaryKey
    private String name;
    
    private int age;
    
    ...
}

關(guān)于設(shè)置主鍵有以下幾點(diǎn)需要注意:

  1. 如果使用copyToRealmOrUpdateinsertOrUpdate方法來創(chuàng)建RealmObject對象的話,則必須為其設(shè)置主鍵逻炊,否則將會跑出異常互亮;
  2. 如果設(shè)置了主鍵,那么查詢速度將會稍微快一點(diǎn)余素,但是插入和更新操作將會稍微變慢豹休,性能的變化取決于數(shù)據(jù)的規(guī)模;
  3. 由于Realm.createObject方法返回的是一個(gè)所有屬性被賦予默認(rèn)值的對象溺森,因此如果有具備有主鍵的RealmObject通過這種方式創(chuàng)建后未被賦值慕爬,那么再次調(diào)用這個(gè)方法來創(chuàng)建具備主鍵的RealmObject將會產(chǎn)生沖突窑眯。解決這個(gè)問題的方式是先創(chuàng)建一個(gè)RealmObject對象屏积,然后給其字段賦值,再調(diào)用copyToRealminsert方法加入數(shù)據(jù)庫磅甩;
  4. 被設(shè)置為主鍵的String類型或包裝類型字段可以被設(shè)置為null炊林,除非同時(shí)設(shè)置@PrimatyKey@Required注解。

2.使用 Realm Object

(a) 對象自動更新

RealmObject是實(shí)時(shí)卷要、自動將視圖更新到基礎(chǔ)數(shù)據(jù)中的渣聚,并且對象的更新能夠立即作用到(已有的)查詢結(jié)果中独榴。

Realm realm = Realm.getDefaultInstance();

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Dog dog1 = realm.createObject(Dog.class);
        dog1.setName("牧羊犬");
        dog1.setAge(2); // age = 2
    }
});

Dog dog2 = realm.where(Dog.class).equalTo("age",2).findFirst();
Log.i("Age","dog age is:"+dog2.getAge()); // 輸出結(jié)果: 2

realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Dog dog3 = realm.where(Dog.class).equalTo("age",2).findFirst();
        dog3.setAge(4); // age = 4
    }
});

Log.i("Age","Now dog age is:"+dog2.getAge()); // 輸出結(jié)果: 4(已經(jīng)不是2了哦)

//以上代碼段經(jīng)運(yùn)行后結(jié)果:
08-15 15:57:21.460 21792-21792/com.chivan.realmdemo I/Age: dog age is:2
08-15 15:57:21.460 21792-21792/com.chivan.realmdemo I/Age: Now dog age is:4

這中特性不僅能夠使得Realm快速、高效奕枝,也能使你的代碼變得更加簡潔和更具有交互性棺榔。更重要的是,如果你的ActivityFragment依賴于某個(gè)特定的RealmObject或者RealmResults實(shí)例隘道,那么更新UI之前無需擔(dān)心數(shù)據(jù)刷新或者重新拉取數(shù)據(jù)症歇。

(b)自定義RealmObject對象

RealmObjectJavaBean類似,我們也可以直接繼承RealmObject谭梗。RealmObject的屬性權(quán)限可以是public忘晤、protected或者private。如果聲明為了public權(quán)限激捏,那么無需定義getter设塔、setter方法,可以直接給屬性賦值远舅。

// 定義 Cat 類闰蛔,繼承自 RealmObject
public class Cat extends RealmObject {
    public String name;
    public String age;

//    注意:name、age均是public權(quán)限图柏,因此無需getter钞护、setter方法
//    public String getName() {
//        return name;
//    }
//
//    public void setName(String name) {
//        this.name = name;
//    }
//
//    public String getAge() {
//        return age;
//    }
//
//    public void setAge(String age) {
//        this.age = age;
//    }
}

// 創(chuàng)建 cat 對象,并給對象屬性賦值
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Cat cat = realm.createObject(Cat.class);
        cat.name = "波斯貓";
        cat.age = 2;
    }
});

Cat類中我們定義了nameage屬性爆办,除此之外還可以添加其他自定義函數(shù)难咕。

(c)RealmModel接口

除了繼承RealmObject外,還可以實(shí)現(xiàn)RealmModel接口距辆,并配合@RealmClass注解余佃。這種情況下,所有RealmObject對象的屬性只能通過靜態(tài)方法來調(diào)用跨算。

// 繼承 RealmObject
cat.deleteFromRealm();
cat.isValid();

//實(shí)現(xiàn) RealmModel 接口
RealmObject.deleteFromRealm(cat);
RealmObject.isValid(cat);

3.插入操作

與讀操作不同的是爆土,Realm的寫操作必須包含在事務(wù)(transaction)中。在事務(wù)的結(jié)尾诸蚕,我們可以選擇提交事務(wù)或者取消事務(wù)步势。事務(wù)一旦被提交,那么事務(wù)作出的所有改變都將會被寫入磁盤背犯。事務(wù)被取消后坏瘩,那么所有的改變都將失效。換句話說漠魏,同一個(gè)事務(wù)中的操作要么全都做倔矾、要么就全都不錯(cuò),這保證了數(shù)據(jù)的一致性和線程安全。

realm.beginTransaction();//開始事務(wù)
// do some things...
realm.commitTransaction();//提交事務(wù)

realm.beginTransaction();//開始事務(wù)
// do some things...
realm.cancelTransaction();//取消事務(wù)

需要注意的是Realm的寫操作會互相死鎖哪自,因此如果你在UI線程和后臺線程中同時(shí)開啟了寫入事務(wù)丰包,那么將可能會導(dǎo)致ANR錯(cuò)誤。為了避免這種事件發(fā)生壤巷,官方建議 在異步事務(wù)(async transaction)處理寫入事務(wù)邑彪。

如果在事務(wù)中發(fā)生了異常,那么事務(wù)中所有的改變都將消失胧华,但是Realm本身不受影響锌蓄。如果捕捉了事務(wù)中的異常,并且程序?qū)^續(xù)執(zhí)行下去撑柔,那么需要取消產(chǎn)生異常的事務(wù)瘸爽。有個(gè)省事的做法就是使用excuteTransaction來執(zhí)行事務(wù),那么產(chǎn)生異常后的后續(xù)操作都將會自動執(zhí)行铅忿,無需人工參與剪决。

得益于RealmMVCC架構(gòu),即使開啟了事務(wù)檀训,我們?nèi)匀荒軌蛲瑫r(shí)開啟讀操作柑潦。當(dāng)你提交了一個(gè)寫事務(wù)后,所有其他Realm實(shí)例都會收到通知并自動更新峻凫。

(a)創(chuàng)建對象

在事務(wù)中用createObject方法創(chuàng)建對象:

realm.beginTransaction(); //開啟事務(wù)
Dog dog = realm.createObject(Dog.class); //創(chuàng)建 Dog 對象
dog.setAge(2);
dog.setName("金毛");
realm.commitTransaction(); //提交事務(wù)

如果是首先創(chuàng)建了一個(gè)對象渗鬼,然后使用copyToRealm將對象寫入到Realm數(shù)據(jù)庫,那么你需要將這個(gè)操作放入事務(wù)中提交荧琼。Realm支持無限個(gè)自定義有參構(gòu)造函數(shù)來創(chuàng)建對象譬胎,但前提是必須至少要有一個(gè)public修飾的無參構(gòu)造函數(shù)。否則將會報(bào)錯(cuò):Class "Cat" must declare a public constructor with no arguments if it contains custom constructors.


Cat cat = new Cat("小奶貓"); // 有參構(gòu)造函數(shù)
cat.setAge(5);
realm.beginTransaction();
Cat catRealm = realm.copyToRealm(cat); //將對象拷貝到 Realm 數(shù)據(jù)庫中
realm.commitTransaction();

這里需要強(qiáng)調(diào)的是只有在返回的對象(上述代碼中的catRealm對象)上的操作會生效命锄,而原始的對象(上述代碼中的cat對象)上的操作將不會同步到Realm數(shù)據(jù)庫中堰乔。如果你僅僅是向Realm數(shù)據(jù)庫中插入一個(gè)對象,而不會立馬對對象做出修改的話脐恩,那就盡可能用insert方法镐侯。insert方法的實(shí)現(xiàn)方式和copyToRealm方法很相似,但是前者速度更快驶冒,因?yàn)樗挥梅祷乇徊迦雽ο蟮母北竟斗H绻胍迦攵鄠€(gè)對象,推薦使用insertinsertOrUpdate方法(所有的插入操作都需要放到事務(wù)塊中)骗污。

List<Cat> cats = Arrays.asList(new Cat("金毛"), new Cat("小奶貓"));
realm.beginTransaction();
realm.insert(cats); //插入多個(gè)Cat對象
realm.commitTransaction();

(b)異步事務(wù)

前文講到如果在UI線程和后臺線程中同時(shí)開啟了寫入事務(wù)崇猫,那么可能會導(dǎo)致ANR。因此身堡,為了避免阻塞UI線程邓尤,我們可以采用異步事務(wù)來避免這個(gè)問題拍鲤。

Realm realm = Realm.getDefaultInstance();
realm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        Cat cat = realm.createObject(Cat.class);
        cat.setName("小黃貓");
        cat.setAge(3);
    }
}, new Realm.Transaction.OnSuccess() {
    @Override
    public void onSuccess() {
        // 寫入成功
    }
}, new Realm.Transaction.OnError() {
    @Override
    public void onError(Throwable error) {
        // 寫入失敗
    }
});

以上代碼中的onSuccessonError回調(diào)參數(shù)是可選的贴谎,如果傳入了相應(yīng)的回調(diào)函數(shù)汞扎,那么出發(fā)之后將會執(zhí)行相應(yīng)回調(diào)函數(shù)。需要注意的是回調(diào)函數(shù)在Lopper線程中執(zhí)行擅这。

(c)對象批量更新

如果對對象某個(gè)字段進(jìn)行批量更新澈魄,最高效的方式是先查詢到所有符合條件的對象,然后調(diào)用RealmResults.setXXX()方法仲翎。

//批量更新對象屬性值
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        RealmResults<Cat> cats = realm.where(Cat.class).equalTo("name","小奶貓").findAll();//查詢所有name = "小奶貓"的對象 
        cats.setValue("age",3); //將符合條件到的所有Cat對象age屬性值設(shè)置為3
    }
});

其中setXXX()函數(shù)的第一個(gè)參數(shù)是對象字段名痹扇,第二個(gè)參數(shù)是字段值。

4.查詢操作

Realm的查詢引擎使用Fluent接口來構(gòu)造多子句查詢溯香。所有提撒旯埂(包括查詢)在Realm中都是懶加載模式,并且永遠(yuǎn)不會復(fù)制數(shù)據(jù)玫坛。例如查詢所有name="小奶貓"并且age = 2的對象结笨,我們可以按如下方式來查詢:

RealmResults<Cat> cats2 = realm.where(Cat.class)
        .equalTo("name","小奶貓")
        .and()
        .equalTo("age",2)
        .findAll();

(a)結(jié)果過濾

where方法通過指定被查詢的model來開始RealmQuery。過濾條件使用謂詞作為名稱的函數(shù)指定湿镀,其中大多數(shù)函數(shù)的名稱不言自明(例如炕吸,equalTo),過濾函數(shù)將字段名稱作為其第一個(gè)參數(shù)勉痴。

使用in函數(shù)從查詢結(jié)果集中匹配相應(yīng)的字段赫模,例如要查找name小奶貓金毛或者小花貓的對象蒸矛,我們可以使用以下語法:

RealmResults<Cat> cats3 = realm.where(Cat.class)
        .in("name", new String[]{"小奶貓","小花貓","金毛"})
        .findAll();

in過濾函數(shù)支持字符串瀑罗、二進(jìn)制數(shù)據(jù)數(shù)值類型日期類型雏掠。其中數(shù)值類型日期類型還額外支持以下幾種過濾操作:

  • between
  • graterThan
  • lessThan
  • greaterThanOrEqualTo
  • lessThanOrEqualTo

字符串類型額外支持以下幾種過濾操作:

  • contains
  • beginsWith
  • endsWith
  • like

其中,字符串類型有額外的第三個(gè)參數(shù)來控制是否大小寫敏感:Case.INSENSITIVECase.SENSITIVE廓脆,默認(rèn)是Case.SENSITIVE

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末磁玉,一起剝皮案震驚了整個(gè)濱河市停忿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蚊伞,老刑警劉巖席赂,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異时迫,居然都是意外死亡颅停,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門掠拳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來癞揉,“玉大人,你說我怎么就攤上這事『笆欤” “怎么了柏肪?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長芥牌。 經(jīng)常有香客問我烦味,道長,這世上最難降的妖魔是什么壁拉? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任谬俄,我火速辦了婚禮,結(jié)果婚禮上弃理,老公的妹妹穿的比我還像新娘溃论。我一直安慰自己,他們只是感情好痘昌,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布蔬芥。 她就那樣靜靜地躺著,像睡著了一般控汉。 火紅的嫁衣襯著肌膚如雪笔诵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天姑子,我揣著相機(jī)與錄音乎婿,去河邊找鬼。 笑死街佑,一個(gè)胖子當(dāng)著我的面吹牛谢翎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沐旨,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼森逮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了磁携?” 一聲冷哼從身側(cè)響起褒侧,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谊迄,沒想到半個(gè)月后闷供,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡统诺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年歪脏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粮呢。...
    茶點(diǎn)故事閱讀 38,643評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡婿失,死狀恐怖钞艇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情豪硅,我是刑警寧澤哩照,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站舟误,受9級特大地震影響葡秒,放射性物質(zhì)發(fā)生泄漏姻乓。R本人自食惡果不足惜嵌溢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蹋岩。 院中可真熱鬧赖草,春花似錦、人聲如沸剪个。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扣囊。三九已至乎折,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侵歇,已是汗流浹背骂澄。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惕虑,地道東北人坟冲。 一個(gè)月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像溃蔫,于是被迫代替她去往敵國和親健提。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容