欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android之Realm详解

程序员文章站 2022-04-24 21:33:13
文章大纲 一、Realm介绍二、Realm实战三、Realm官方文档四、项目源码下载五、参考文章 一、Realm介绍 1. 什么是Realm Realm 是一个手机数据库,是用来替代 SQlite 的解决方案,比 SQlite 更轻量级,速度更快,因为它有一套自己的数据库搜索引擎,并且还具有很多现代 ......

文章大纲

一、realm介绍
二、realm实战
三、realm官方文档
四、项目源码下载
五、参考文章

 
Android之Realm详解

一、realm介绍

1. 什么是realm

  realm 是一个手机数据库,是用来替代 sqlite 的解决方案,比 sqlite 更轻量级,速度更快,因为它有一套自己的数据库搜索引擎,并且还具有很多现代数据库的优点,支持 json,流式 api 调用,数据变更通知,自动数据同步,简单身份验证,访问控制,事件处理,最重要的是跨平台,目前已经支持 java、swift、object - c、react - native 等多种实现。

2. realm优势

易用
  ream 不是在sqlite基础上的orm,它有自己的数据查询引擎。并且十分容易使用。
快速
  由于它是完全重新开始开发的数据库实现,所以它比任何的orm速度都快很多,甚至比slite速度都要快。
跨平台
  realm 支持 ios & os x (objective‑c & swift) & android。我们可以在这些平台上共享realm数据库文件,并且上层逻辑可以不用任何改动的情况下实现移植。
高级
  ream支持加密,格式化查询,易于移植,支持json,流式api,数据变更通知等高级特性
可视化
  realm 还提供了一个轻量级的数据库查看工具,在mac appstore 可以下载“realm browser”这个工具,开发者可以查看数据库当中的内容,执行简单的插入和删除数据的操作。(windows上还不清楚)

3. 使用要求

(1)目前不支持android以外的java
(2)android studio >= 1.5.1(不支持eclipse)
(3)jdk version >=7.
(4)支持api 9(android 2.3)以及之后的版本

二、realm实战

1. 添加依赖

在project的build中加入依赖

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "io.realm:realm-gradle-plugin:2.2.1"
    }
}
 
Android之Realm详解
image

在module中加入

apply plugin: 'realm-android'
 
Android之Realm详解

2. 创建model

  创建一个user类,需要继承realmobject。支持public, protected和 private的类以及方法

public class user 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来声明 realm 数据模型之外,还可以通过实现 realmmodel接口并添加 @realmclass修饰符来声明。

@realmclass
public class user implements realmmodel {
    ...
}

支持的属性
boolean, byte, short,int,long,float, double,string, date 和,byte[], realmobject, realmlist<? extends realmobject>
还支持boolean, byte, short, integer, long, float 和 double
提示:整数类型 short、int 和 long 都被映射到 realm 内的相同类型(实际上为 long )
(1)@primarykey——表示该字段是主键
  使用过数据库的同学应该看出来了,primarykey就是主键。使用@primarykey来标注,字段类型必须是字符串(string)或整数(byte,short,int或long)以及它们的包装类型(byte,short, integer, 或 long)。不可以存在多个主键,使用字符串字段作为主键意味着字段被索引(注释@primarykey隐式地设置注释@index)。

@primarykey
private string id;

(2)@required——表示该字段非空
  在某些情况下,有一些属性是不能为null的。使用@required可用于用于强行要求其属性不能为空,只能用于boolean, byte, short, integer, long, float, double, string, byte[] 和 date。在其它类型属性上使用。

(3)@required修饰会导致编译失败。
  提示:基本数据类型不需要使用注解 @required,因为他们本身就不可为空。

@required
private string name;

(4)@ignore——表示忽略该字段
  被添加@ignore标签后,存储数据时会忽略该字段。

@ignore
private string name;

(5)@index——添加搜索索引
  为字段添加搜索索引,这样会使得插入的速度变慢,数据量也变得更大。不过在查询速度将变得更快,建议只在优化读取性能的特定情况时添加索引。支持索引:string,byte,short,int,long,boolean和date字段。

注意:如果你创建model并运行过,然后修改了model。那么就需要升级数据库,否则会抛异常。升级方式后面会提到

3. 初始化realm

重写application类

public class myapp extends application {
    @override
    public void oncreate() {
        super.oncreate();
}
}

androidmanifest.xml中配置myapp

 
Android之Realm详解

创建方式1

public class myapp extends application {
    @override
    public void oncreate() {
        super.oncreate();

        realm.init(this);//初始化realm

        realm mrealm = realm.getdefaultinstance();
    }
}

  这时候会创建一个叫做 default.realm的realm文件,一般来说,这个文件位于/data/data/包名/files/。通过realm.getpath()来获得该realm的绝对路径。

创建方式2

public class myapp extends application {
    @override
    public void oncreate() {
        super.oncreate();

        realm.init(this);//初始化realm

        //这时候会创建一个叫做 default.realm的realm文件,一般来说,
        // 这个文件位于/data/data/包名/files/。通过realm.getpath()来获得该realm的绝对路径
        realmconfiguration config = new realmconfiguration.builder()
                .name("myrealm.realm") //文件名
                .schemaversion(0) //版本号
                .build();
        realm realm = realm.getinstance(config);

    }

创建方式3

public class myapp extends application {
    @override
    public void oncreate() {
        super.oncreate();

        realm.init(this);//初始化realm


        //创建保存在内存中的realm,应用关闭后就清除了
//        realmconfiguration myconfig = new realmconfiguration.builder(this)
//                .name("myrealm.realm")
//                .inmemory() .build();

    }

创建方式4

public class myapp extends application {
    @override
    public void oncreate() {
        super.oncreate();

        realm.init(this);//初始化realm

        
        //创建加密的realm
//        byte[] key = new byte[64];
//        new securerandom().nextbytes(key);
//        realmconfiguration config = new realmconfiguration.builder()
//                .encryptionkey(key)
//                .build();
//        realm realm = realm.getinstance(config);

    }
}

总结:
realmconfiguration支持的方法:
builder.name : 指定数据库的名称。如不指定默认名为default。
builder.schemaversion : 指定数据库的版本号。
builder.encryptionkey : 指定数据库的密钥。
builder.migration : 指定迁移操作的迁移类。
builder.deleterealmifmigrationneeded : 声明版本冲突时自动删除原数据库。
builder.inmemory : 声明数据库只在内存中持久化。
build : 完成配置构建。

4. 关闭realm

记得使用完后,在ondestroy中关闭realm

@override 
protected void ondestroy() { 
    super.ondestroy();
    // close the realm instance. 
    realm.close(); 
}

5. 获取realm对象

public class realmhelper {
    public static final string db_name = "myrealm.realm";
    private realm mrealm;


    public realmhelper(context context) {

        mrealm = realm.getdefaultinstance();
    }

public realm getrealm(){

        return mrealm;
    }

6. 增加数据

  写入操作需要在事务中进行,可以使用executetransaction方法来开启事务。
(1)使用executetransaction方法插入数据

mrealm.executetransaction(new realm.transaction() {
            @override
            public void execute(realm realm) {
                user user = realm.createobject(user.class);
                user.setname("gavin");
                user.setage(23);
            }
        });

  注意:如果在ui线程中插入过多的数据,可能会导致主线程拥塞。

(2)使用copytorealmorupdate或copytorealm方法插入数据
当model中存在主键的时候,推荐使用copytorealmorupdate方法插入数据。如果对象存在,就更新该对象;反之,它会创建一个新的对象。若该model没有主键,使用copytorealm方法,否则将抛出异常。

 final user user = new user();
        user.setname("jack");
        user.setid("2");
        mrealm.executetransaction(new realm.transaction() {
            @override
            public void execute(realm realm) {
                realm.copytorealmorupdate(user);
            }
        });

如果你用的是这样的modle

public class user2 extends realmobject {
      public string name;
      public int age;
}

就这样写

mrealm.executetransaction(new realm.transaction() {
            @override
            public void execute(realm realm) {
                user2 user = realm.createobject(user2.class);
                user.name = "micheal";
                user.age = 30;
            }
        });

(3)上面都是用可executetransaction方法插入数据,还有另一种方法可以用于插入数据——begintransaction和committransaction

mrealm.begintransaction();//开启事务
user user = mrealm.createobject(user.class);
user.setname("gavin");
user.setid("3");
mrealm.committransaction();//提交事务

  在插入前,先调用begintransaction(),完成后调用committransaction()即可。
  注意:在ui和后台线程同时开启创建write的事务,可能会导致anr错误。为了避免该问题,可以使用executetransactionasync来实现。

(4)使用executetransactionasync
该方法会开启一个子线程来执行事务,并且在执行完成后进行结果通知。

realmasynctask transaction = mrealm.executetransactionasync(new realm.transaction() {
    @override
    public void execute(realm realm) {
        user user = realm.createobject(user.class);
        user.setname("eric");
        user.setid("4");
      }
});

还可以加入监听

realmasynctask transaction =  mrealm.executetransactionasync(new realm.transaction() {
    @override
    public void execute(realm realm) {
        user user = realm.createobject(user.class);
        user.setname("eric");
        user.setid("4");
      }
}, new realm.transaction.onsuccess() {
    @override
    public void onsuccess() {
        //成功回调
      }
}, new realm.transaction.onerror() {
    @override
    public void onerror(throwable error) {
        //失败回调
      }
});

  注意:如果当acitivity或fragment被销毁时,在onsuccess或onerror中执行ui操作,将导致程序奔溃 。用realmasynctask .cancel();可以取消事务在onstop中调用,避免crash

public void onstop () {
    if (transaction != null && !transaction.iscancelled()) {
        transaction.cancel();
      }
}

(5)realm还是个很好的功能就是将json字符串转化为对象

// 一个city model
public class city extends realmobject {
    private string city;
    private int id;
    // getters and setters left out ...
}
// 使用json字符串插入数据
realm.executetransaction(new realm.transaction() {
    @override
    public void execute(realm realm) {
        realm.createobjectfromjson(city.class, "{ city: \"copenhagen\", id: 1 }");
    }
});
// 使用inputstream插入数据
realm.executetransaction(new realm.transaction() {
    @override
    public void execute(realm realm) {
        try {
            inputstream is = new fileinputstream(new file("path_to_file"));
            realm.createallfromjson(city.class, is);
        } catch (ioexception e) {
            throw new runtimeexception();
        }
    }
});

realm 解析 json 时遵循如下规则:
使用包含空值(null)的 json 创建对象:
对于非必须(可为空值的属性),设置其值为 null;
对于必须(不可为空值的属性),抛出异常;
使用包含空值(null)的 json 更新对象:
对于非必须(可为空值的属性),设置其值为 null;
对于必须(不可为空值的属性),抛出异常;
使用不包含对应属性的 json: * 该属性保持不变

7. 查询数据

查找操作就比插入方便多了,并不需在事务中操作,直接查询即可。
(1)findall ——查询
例:查询所有的user

realmresults<user> userlist = mrealm.where(user.class).findall();

(2)findallasync——异步查询
当数据量较大,可能会引起anr的时候,就可以使用findallasync

realmresults<user> userlist = mrealm.where(user.class)
                .equalto("name", "gavin")
                .findallasync();

(3)findfirst ——查询第一条数据
例:查询user表中的第一条数据

user user2 = mrealm.where(user.class).findfirst();

(4)equalto ——根据条件查询
例:得到name为gavin的用户列表。

realmresults<user> userlist = mrealm.where(user.class)
            .equalto("name", "gavin").findall();

如果user中还有dog属性,希望根据dog的条件来获取用户:
例:查询dogs.name为二哈的user

realmresults<user> userlist = mrealm.where(user.class)
             .equalto("dogs.name", "二哈").findall();

  得到有养有dogs.name为”二哈”的用户列表(这里的dogs是user表中的属性名)

(5)equalto ——多条件查询
当然,我们还经常要用到多条件的查询的功能。
例:找到用户名为“gavin”,且dogs.name为“二哈”的user

realmresults<user> userlist = mrealm.where(user.class)
            .equalto("name", "gavin").findall();
realmresults<user> userlist = user5.where()
            .equalto("dogs.name", "二哈").findall();

(6)排序
查询结束后,还可以进行排序,就像这样:

realmresults<user> userlist = mrealm.where(user.class) .findall();
userlist = result.sort("age"); //根据age,正序排列
userlist = result.sort("age", sort.descending);//逆序排列

(7)聚合
realmresult自带一些聚合方法:

realmresults<user> results = realm.where(user.class).findall();
long   sum     = results.sum("age").longvalue();
long   min     = results.min("age").longvalue();
long   max     = results.max("age").longvalue();
double average = results.average("age");
long   matches = results.size();

(8)更多查询条件
  上面就展示了equalto的用法。当然,查询还有更多的用法,我就不一一示例了。已知的方法如下:
sum():对指定字段求和。
average():对指定字段求平均值。
min(): 对指定字段求最小值。
max() : 对指定字段求最大值。count : 求结果集的记录数量。
findall(): 返回结果集所有字段,返回值为realmresults队列
findallsorted() : 排序返回结果集所有字段,返回值为realmresults队列
between(), greaterthan(),lessthan(), greaterthanorequalto() & lessthanorequalto()
equalto() & notequalto()
contains(), beginswith() & endswith()
isnull() & isnotnull()
isempty()& isnotempty()

8. 修改数据

mrealm.executetransaction(new realm.transaction() {
    @override
    public void execute(realm realm) {
        //先查找后得到user对象
        user user = mrealm.where(user.class).findfirst();
        user.setage(26);
    }
});

修改也是需要在事务中操作。使用查询语句得到数据,然后将内容改了即可。

9. 删除数据

(1)使用deletefromrealm()

//先查找到数据
final realmresults<user> userlist = mrealm.where(user.class).findall();
mrealm.executetransaction(new realm.transaction() {
    @override
    public void execute(realm realm) {
        userlist.get(0).deletefromrealm();
    }
});

(2)使用deletefromrealm(int index)

mrealm.executetransaction(new realm.transaction() {
    @override
    public void execute(realm realm) {
        userlist.deletefromrealm(0);
    }
});

(3)其他方法

userlist.deletefirstfromrealm(); //删除user表的第一条数据
userlist.deletelastfromrealm();//删除user表的最后一条数据
results.deleteallfromrealm();//删除user表的全部数据

10. 异步任务注意点

最后在销毁activity或fragment时,要取消掉异步任务

@override
    protected void ondestroy() {
        super.ondestroy();
       if (deletetask!=null&&!addtask.iscancelled()){
            deletetask.cancel();
        }
    }

11. 其他常见操作

api:realmobjectschema
(1)取消id必填

personschema.setnullable("id", true):

(2)移除id字段

personschema.removefield("id");

(3)重命名

personschema..renamefield("id", "userid");

api:dynamicrealmobject
(4)获取id

string id = obj.getstring("id");

(5)为字段设置值

obj.setstring("name", "gavin");
obj.setint("id", 1);
obj.setlong("id", 1);

12. 数据库版本升级

  当数据结构发生变化是,需要升级数据库。对于realm来说,数据库升级就是迁移操作,把原来的数据库迁移到新结构的数据库

  例1:user类发生变化,移除age,新增个@required的id字段。
user版本:version 0

string name;
int    age;

user版本:version 1

@required
string    id;
string name;

创建迁移类custommigration,需要实现realmmigration接口。执行版本升级时的处理:

 /**
     * 升级数据库
     */
    class custommigration implements realmmigration {
        @override
        public void migrate(dynamicrealm realm, long oldversion, long newversion) {
            realmschema schema = realm.getschema();
            if (oldversion == 0 && newversion == 1) {
                realmobjectschema personschema = schema.get("user");
                //新增@required的id
                personschema
                        .addfield("id", string.class, fieldattribute.required)
                        .transform(new realmobjectschema.function() {
                            @override
                            public void apply(dynamicreal
mobject obj) {
                                obj.set("id", "1");//为id设置值
                            }
                        })
                        .removefield("age");//移除age属性
                oldversion++;
            }
        }
    }

使用builder.migration升级数据库,将版本号改为1(原版本号:0)。当realm发现新旧版本号不一致时,会自动使用该迁移类完成迁移操作。

realmconfiguration config = new realmconfiguration.builder() 
            .name("myrealm.realm") //文件名
            .schemaversion(1) 
            .migration(new custommigration())//升级数据库
            .build();

例2:加入dog类,user中加入dog集合。
user版本:version 1

@required
string    id;
string name;

user版本:version 2

@required
private string id;
private string name;
private realmlist<dog> dogs;

dog类

public class dog extends realmobject {
        private string name;
        private int age;
}

在迁移类custommigration中,继续添加处理方法。

(oldversion == 1 && newversion == 2) {
                //创建dog表
                realmobjectschema dogschema = schema.create("dog");
                dogschema.addfield("name", string.class);
                dogschema.addfield("age", int.class);

                //user中添加dogs属性
                schema.get("user")
                        .addrealmlistfield("dogs", dogschema)
                        .transform(new realmobjectschema.function() {
                            @override
                            public void apply(dynamicrealmobject obj) {
                                //为已存在的数据设置dogs数据
                                dynamicrealmobject dog = realm.createobject("dog");
                                dog.set("name", "二哈");
                                dog.set("age", 2);
                                obj.getlist("dogs").add(dog);
                            }
                        });
                oldversion++;
            }

三、realm官方文档

https://realm.io/cn/docs/java/latest/

四、项目源码下载

链接:https://pan.baidu.com/s/1wsoxuclz-nwntnw_79xkla
密码:oc95

五、参考文章

    1. https://blog.csdn.net/u013651026/article/details/79481615