文章大纲

一、Realm介绍
二、Realm实战
三、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"
}
}
 
image

在module中加入

apply plugin: 'realm-android'
 

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

 

创建方式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
    2. https://www.jianshu.com/p/28912c2f31db

Android之Realm详解的更多相关文章

  1. android:ToolBar详解

    android:ToolBar详解(手把手教程) 泡在网上的日子 发表于 2014-11-18 12:49 第 124857 次阅读 ToolBar 42 来源 http://blog.mosil.b ...

  2. Android之canvas详解

    首先说一下canvas类: Class Overview The Canvas class holds the "draw" calls. To draw something, y ...

  3. 【转】Android Canvas绘图详解(图文)

    转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android Canvas绘图详解(图文) 泡 ...

  4. Android 核心分析 之八Android 启动过程详解

    Android 启动过程详解 Android从Linux系统启动有4个步骤: (1) init进程启动 (2) Native服务启动 (3) System Server,Android服务启动 (4) ...

  5. Android GLSurfaceView用法详解(二)

    输入如何处理       若是开发一个交互型的应用(如游戏),通常需要子类化 GLSurfaceView,由此可以获取输入事件.下面有个例子: java代码: package eoe.ClearTes ...

  6. Android编译过程详解(一)

    Android编译过程详解(一) 注:本文转载自Android编译过程详解(一):http://www.cnblogs.com/mr-raptor/archive/2012/06/07/2540359 ...

  7. android屏幕适配详解

    android屏幕适配详解 官方地址:http://developer.android.com/guide/practices/screens_support.html 一.关于布局适配建议 1.不要 ...

  8. Android.mk文件详解(转)

    源:Android.mk文件详解 从对Makefile一无所知开始,折腾了一个多星期,终于对Android.mk有了一个全面些的了解.了解了标准的Makefile后,发现Android.mk其实是把真 ...

  9. Android Studio 插件开发详解四:填坑

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/78265540 本文出自[赵彦军的博客] 在前面我介绍了插件开发的基本流程 [And ...

随机推荐

  1. 结构化您的Python工程

    我们对于"结构化"的定义是您关注于怎样使您的项目最好地满足它的对象性,我们 需要去考虑如何更好地利用Python的特性来创造简洁.高效的代码.在实践层面, "结构化&qu ...

  2. Linux时间子系统之三:时间的维护者:timekeeper

    专题文档汇总目录 Notes: 原文地址:Linux时间子系统之三:时间的维护者:timekeeper 本系列文章的前两节讨论了用于计时的时钟源:clocksource,以及内核内部时间的一些表示方法 ...

  3. Git协作流程

    Git 作为一个源码管理系统,不可避免涉及到多人协作. 协作必须有一个规范的流程,让大家有效地合作,使得项目井井有条地发展下去."协作流程"在英语里,叫做"workflo ...

  4. Load balancer does not have available server for client

    最近在研究spring-cloud,研究zuul组件时发生下列错误: Caused by: com.netflix.client.ClientException: Load balancer does ...

  5. How nginx "location if" works

    Nginx's if directive does have some weirdness in practice. And people may misuse it when they do not ...

  6. rabbit入门教程

    简介 rabbitmq是一个消息代理系统,为应用提供一个通用得消息发布,接受平台,为应用提供非阻塞的消息系统,方便进行异步处理. 优点 消息的可靠性.持久化消息,消息接受确认,消息重传等可靠机制. 灵 ...

  7. python之six用法

    six.PY2 返回一个表示当前运行环境是否为python2的boolean值 six.PY3 返回一个表示当前运行环境是否为python3的boolean值 import six,sys print ...

  8. java语言为什么能跨平台

    参考https://blog.csdn.net/woailuo453786790/article/details/51660015 因为Java程序编译之后的代码不是能被硬件系统直接运行的代码,而是一 ...

  9. Java报错信息 java.lang.SecurityException: Prohibited package name: java.xxx

    package java.yun.System; public class SystemOut { public static void main(String[] args) { System.ou ...

  10. turtle文库 ——python

    本文将会为您介绍关于python--turtle库函数,学会这个库函数,会有很多让你意想不到的事情发生哦! 我也也会为你们,简单的编写几个代码,让你们看一下turtle函数的魅力 Turtle库是Py ...