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接口的,如图:


Realm使用学习笔记_第1张图片
RealmObject.png

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

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;    
    }
}

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


        ...
    

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>() {    
    @Override    
    public void onChange(RealmResults 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 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 r = realm.where(User.class)
            .greaterThan("age", 10) //implicit AND 
            .beginGroup() 
                .equalTo("name", "Peter")
                 .or() 
                .contains("name", "Jo") 
            .endGroup()
            .findAll();

//  排序
RealmResults 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 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

你可能感兴趣的:(Realm使用学习笔记)