Realm使用学习笔记

Realm介绍与引入

其实Realm也已经作为移动端数据库被使用也有一段时间了,而如果作为新手想要把App中数据存储到本地,可能我们File、SP、SQLite,今天要讲的Realm就是代替的SQLite的方法之一,当然还有GreenDao等等,我知道Realm是七月底的时候,各个公众号开始介绍和使用它,而我开始使用它却是从上周才开始的。

我在开始学习Realm的时候就是一口气把官方的文档看完,而现在最新的版本是2.2.1,官方文档也会有中文的比如2.1.1,所以我觉得大家可以一边看英文的一边看中文的,还能锻炼自己的英文阅读能力,毕竟Android官方文档大部分都是英文的,所以在学习新技术的时候就可以看得出英文阅读能力到底有多重要。 下面就开始我们Realm的使用之旅吧!

不过所有这种第三方的开源库使用的第一步都是先添加依赖,然后开始使用。

1.在整个项目的build.gradle下的dependencies添加:
     classpath "io.realm:realm-gradle-plugin:2.2.0"
2.在app目录下的build.gradle添加:
     apply plugin: 'realm-android'

Realm基础

接下来在使用之前我们可以先来看一些基础概念:
1.比如我们如何定义一个数据模型,我们可以自定义一个类继承RealmObject或者实现RealmModel接口并在类上面添加@RealmClass这个注解,这时我们table就会在运行时自动创建了,代码如下:

public class TestModel extends RealmObject{
        ...
} 
or
@RealmClass
public class OtherTestModel implements RealmModel{
        ...
}

当我们点进RealmObject时会发现其实它也是实现RealmModel接口的,如图:


RealmObject.png

2.我们创建了数据模型后还需要添加字段,这样才能真的组成一个table,Realm支持的类型如下:boolean、byte、int、long、float、double(以及他们的封装类)、String、Date、byte[]、RealmObject、RealmList<? extends RealmObject>,不过成员变量的修饰符并没有限制。

3.一些属性:

  • 主键 @PrimaryKey,但是Realm不支持自增长的主键,我一般使用的时候都是定义一个String类型的主键,然后通过UUID保证主键的唯一性(如果封装类作为主键,主键可为null,除非同时被非空修饰)。
  • 非空 @Required
  • 忽略 @Ignore 如果该类中的某个字段被它修饰了就表明它不会被保存到Realm中。
  • 索引 @Index 为字段增加搜索索引,这会导致插入速度变慢,同时数据文件体积有所增加,但能加速查询。

Realm使用

那接下来我们就可以开始使用Realm了,在代码中使用Realm前还需要对它进行初始化Realm.init(上下文对象);我是在全局的Application类中就行的,代码如下

public class MyApplication extends Application {   
  private static Context mContext;    
  @Override    
  public void onCreate() {        
        mContext = getApplicationContext();        
        Realm.init(mContext);    
    }    
    public static Context getContext() {        
        return mContext;    
    }
}

然后再在清单文件中声明一下就可以了

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

1.准备

//  创建实体类  该类必须包含一个无参构造
public class TestModel extends RealmObject{    
    @PrimaryKey    
    private String _id;    
    private String testTitle;    
    private String updateTime;
    ...
}

//  如下配置的 Realm 会被存储在 Context.getFilesDir() 并且命名为 default.realm
//  获取Realm实例 获取默认配置的Realm
Realm mRealm = Realm.getDefaultInstance();

//  事务 无论是增删改查都需要开启事务
mRealm.beginTransaction();
//  具体逻辑
...
mRealm .commitTransaction();
//  如果要取消事务的话
mRealm .cancelTransaction();

//  为result设置数据改变侦听  赋值的话我是先调用查询 查询就算没有条目也不会返回null,所以无须担心
mResults.addChangeListener(new RealmChangeListener<RealmResults<TestModel>>() {    
    @Override    
    public void onChange(RealmResults<TestModel> element) {        
        //  这里无需再次赋值给RealmResults, 因为他会自动更新值(必须要当前前程有Looper, 而当前是主线程)       
        mResults = element;        
        mAdapter.notifyDataSetChanged();    
     }
});

2.写入

/**     
 * 插入假数据 这种方式会产生默认值的对象,然后手动设置值,且如果有主键时需要在createObject中设置 在后台线程进行     
 */    
private void createItemData() {        
    //  executeTransaction系列方法都会自动管理事务 开启,关闭,取消     
    mRealm.executeTransactionAsync(new Realm.Transaction() {    
        @Override            
        public void execute(Realm realm) {                
            UUID uuid = UUID.randomUUID();   
            //  写入的时候指明了Id             
            TestModel testModel = realm.createObject(TestModel.class, uuid.toString());                
            testModel.setTestTitle("点击编辑标题");
            testModel.setUpdateTime(getCurrTime());            
        }        
    }, new Realm.Transaction.OnSuccess() {            
         @Override            
         public void onSuccess() {
             mAdapter.notifyDataSetChanged();            
        }        
    }, new Realm.Transaction.OnError() {            
        @Override            
        public void onError(Throwable error) {               
             Log.e("Realm", "保存失败" + error.getMessage());            
        }        
    });    
}

//  插入的话还有另一种方法copyToRealm();
//  这个Dog对象只是一个普通的对象
Dog dog = new Dog();
dog.setName("Rex");
dog.setAge(1);
mRealm.beginTransaction();
//  而这边返回的是managedDog 才是被持久化的对象
final managedDog = realm.copyToRealm(dog);
mRealm.commitTransaction();

3.查询

//  查询所有  TestModel是我自己写的类
RealmResults<TestModel> mResults = mRealm.where(TestModel.class).findAllAsync();

/** 
 * 根据标题查询数据 在UI线程进行 
 */
private void queryResultByTitle(String title) {  
    //  根据Title进行模糊查询  
    mResults = mRealm.where(TestModel.class).contains("testTitle", title)            .findAll();    
    mAdapter.notifyDataSetChanged();
}


//  支持的查询的条件  当然也支持聚合函数
- between()、greaterThan()、lessThan()、greaterThanOrEqualTo() 和 lessThanOrEqualTo()  
- equalTo() 和 notEqualTo()  
- contains()、beginsWith()和 endsWith()  (类似模糊查寻)
- isNull() 和 isNotNull()
- isEmpty() 和 isNotEmpty()

//  每个查询条件都会被被隐式地被逻辑和(&)组合在一起,而逻辑或(or)需要显式地去执行 or()  如下:
RealmResults<User> r = realm.where(User.class)
            .greaterThan("age", 10) //implicit AND 
            .beginGroup() 
                .equalTo("name", "Peter")
                 .or() 
                .contains("name", "Jo") 
            .endGroup()
            .findAll();

//  排序
RealmResults<User> result = realm.where(User.class).findAll();
result = result.sort("age");  //  默认正序
result = result.sort("age", Sort.DESCENDING);

4.删除

/** 
 * 删除数据 在后台线程进行 
 */
private void deleteItemData(final int index) {
    mRealm.executeTransaction(new Realm.Transaction() {        
        @Override        
        public void execute(Realm realm) {
            mResults.deleteFromRealm(index);        
        }    
    });
}

//  删除的方法还有很多就不一一列举了

5.更新

/**
 * 更新数据方式一 通过再次查询 在后台线程进行
 */
private void updateItemDataById(final String title) {
    mRealm.executeTransactionAsync(new Realm.Transaction() {
        @Override        
        public void execute(Realm realm) {
            TestModel testModel = realm.where(TestModel.class).equalTo("_id", mCurrEditId).findFirst();            
            testModel.setUpdateTime(getCurrTime());            testModel.setTestTitle(title);        
        }    
    }, new Realm.Transaction.OnSuccess() {        
        @Override        
        public void onSuccess() {            
            mAdapter.notifyDataSetChanged();        
        }    
    }, new Realm.Transaction.OnError() {        
        @Override        
        public void onError(Throwable error) {            
            Log.e("Realm", "保存失败" + error.getMessage());        
        }    
    });
}

/**
 * 更新数据方法二 通过Realm的特性(实时更新) 在创建RealmResults的线程进行   
 */
private void updateItemData(final String title) {
    mRealm.executeTransaction(new Realm.Transaction() {        
        @Override        
        public void execute(Realm realm) {
            mResults.get(mCurrItemIndex).setTestTitle(title);
            mResults.get(mCurrItemIndex).setUpdateTime(getCurrTime());
        }    
    });    
    mAdapter.notifyDataSetChanged();
}

6.数据库配置

//  Realm的配置类
RealmConfiguration config = new RealmConfiguration.Builder()
            .name("test.realm")     //  命名      
            .schemaVersion(3)      //  数据库版本        
            .migration(new MyMigration())    //  数据库内容发生变化    
            .build();
//  获取Realm对象并手动设置配置
mRealm = Realm.getInstance(config);

/**
 * 当我们更新数据库字段时会使用这个类
 */
public class MyMigration implements RealmMigration {    
    @Override    
    public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {        
        RealmSchema schema = realm.getSchema();        
        //  比如当老的版本为3的时候        
        if (oldVersion == 3) {            
              schema.get("TestModel")                    
                      //  添加新的Realm支持的字段
                      .addField("testBassType", int.class)                    
                      //  添加新的自定义对象
                      .addRealmObjectField("testRealmObject", schema.get("OtherTestModel"))                    
                      //  添加新的List对象,用于一对多 多对多
                      .addRealmListField("testRealmList", schema.get("OtherTestModel"));            
                      oldVersion++;        
        }    
    }
}

//  而我在相应的类中已经写了
//  用于测试数据库字段变更 版本变成4的时候放开下面的字段
//    public int testBassType;
//    public OtherTestModel testRealmObject;
//    public RealmList<OtherTestModel> testRealmList;

7.释放资源

@Override
protected void onDestroy() {    
    super.onDestroy();    
    //  移除侦听 并关闭Realm    
    mResults.removeChangeListeners();    
    mRealm.close();}

Realm知识点补充

1.异步查询可写回调的侦听,且其回调是通过Looper被执行的(如写入那条)。

2.Realm的实体类必须要包含一个无参构造函数,如果不写任何构造,Java自带一个无参构造,若是自己写了其他参数的构造后无参构造会不可用,此时需要手动头添加一个。

3.主键不支持自增长。

4.使用查询后的所返回的RealmResults<自定义类>对象列表,里面的对象并非是拷贝,而是查询后匹配对象的引用,所以删改都可以直接用它来操作。

5.每个查询条件都会被被隐式地被逻辑和(&)组合在一起,而逻辑或(or)需要显式地去执行 or()。

6.异步操作可以对RealmResults设置addChangeLintener(Callback),侦听结果集发生的变化,记得最后要移除掉侦听。

7.异步查询可用isLoaded()检查是否加载完毕,而同步时该方法永远返回true。

8.Realm、RealmObject 和RealmResults 实例都不可以跨线程使用,而获取后都是获取当前线程的实例,跨线程使用会报错。

9.Realm实例是基于引用计数的,所以你在同一个线程中获取了几次实例,也需要相应的close()几次。

10.如果Realm实例存在于一个带有Looper的线程,那么这个Realm实例就具有自动刷新的功能,这个时候就可以使用,且Listener只工作于 Looper线程。

11.如果Realm实例所在的线程没有Looper,则更新需要你手动调用waitForChange()。

我希望可以站在初学者&自学者的角度把Android中的知识点很清楚的介绍给大家,希望大家喜欢。 如果有错误希望指出来,有问题或者没看懂,都可以来问我的

    项目代码的地址:https://github.com/GzwJaaaelu/RealmDemo
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容