Android之Realm详解
Android之Realm详解
文章大纲
一、Realm介绍
二、Realm实战
三、Realm官方文档
四、项目源码下载
五、参考文章
一、Realm介绍
- 什么是Realm
Realm 是一个手机数据库,是用来替代 SQlite 的解决方案,比 SQlite 更轻量级,速度更快,因为它有一套自己的数据库搜索引擎,并且还具有很多现代数据库的优点,支持 JSON,流式 API 调用,数据变更通知,自动数据同步,简单身份验证,访问控制,事件处理,最重要的是跨平台,目前已经支持 Java、Swift、Object - C、React - Native 等多种实现。 - Realm优势
易用
Ream 不是在SQLite基础上的ORM,它有自己的数据查询引擎。并且十分容易使用。
快速
由于它是完全重新开始开发的数据库实现,所以它比任何的ORM速度都快很多,甚至比SLite速度都要快。
跨平台
Realm 支持 iOS & OS X (Objective‑C & Swift) & Android。我们可以在这些平台上共享Realm数据库文件,并且上层逻辑可以不用任何改动的情况下实现移植。
高级
Ream支持加密,格式化查询,易于移植,支持JSON,流式api,数据变更通知等高级特性
可视化
Realm 还提供了一个轻量级的数据库查看工具,在Mac Appstore 可以下载“Realm Browser”这个工具,开发者可以查看数据库当中的内容,执行简单的插入和删除数据的操作。(windows上还不清楚)
- 使用要求
(1)目前不支持Android以外的Java
(2)Android Studio >= 1.5.1(不支持Eclipse)
(3)JDK version >=7.
(4)支持API 9(Android 2.3)以及之后的版本
二、Realm实战
- 添加依赖
在project的build中加入依赖
buildscript {
repositories { jcenter() } dependencies { classpath "io.realm:realm-gradle-plugin:2.2.1" }
}
image
在module中加入
apply plugin: 'realm-android'
- 创建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。那么就需要升级数据库,否则会抛异常。升级方式后面会提到
- 初始化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 : 完成配置构建。
- 关闭Realm
记得使用完后,在onDestroy中关闭Realm
@Override
protected void onDestroy() {
super.onDestroy(); // Close the Realm instance. realm.close();
}
- 获取realm对象
- 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; }
- 增加数据
写入操作需要在事务中进行,可以使用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: * 该属性保持不变
- 查询数据
查找操作就比插入方便多了,并不需在事务中操作,直接查询即可。
(1)findAll ——查询
例:查询所有的User
RealmResults userList = mRealm.where(User.class).findAll();
(2)findAllAsync——异步查询
当数据量较大,可能会引起ANR的时候,就可以使用findAllAsync
RealmResults userList = mRealm.where(User.class)
.equalTo("name", "Gavin") .findAllAsync();
(3)findFirst ——查询第一条数据
例:查询User表中的第一条数据
User user2 = mRealm.where(User.class).findFirst();
(4)equalTo ——根据条件查询
例:得到name为Gavin的用户列表。
RealmResults userList = mRealm.where(User.class)
.equalTo("name", "Gavin").findAll();
如果User中还有Dog属性,希望根据Dog的条件来获取用户:
例:查询dogs.name为二哈的User
RealmResults userList = mRealm.where(User.class)
.equalTo("dogs.name", "二哈").findAll();
得到有养有dogs.name为”二哈”的用户列表(这里的dogs是User表中的属性名)
(5)equalTo ——多条件查询
当然,我们还经常要用到多条件的查询的功能。
例:找到用户名为“Gavin”,且dogs.name为“二哈”的User
RealmResults userList = mRealm.where(User.class)
.equalTo("name", "Gavin").findAll();
RealmResults userList = user5.where()
.equalTo("dogs.name", "二哈").findAll();
(6)排序
查询结束后,还可以进行排序,就像这样:
RealmResults userList = mRealm.where(User.class) .findAll();
userList = result.sort("age"); //根据age,正序排列
userList = result.sort("age", Sort.DESCENDING);//逆序排列
(7)聚合
RealmResult自带一些聚合方法:
RealmResults 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()
-
修改数据
mRealm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {//先查找后得到User对象 User user = mRealm.where(User.class).findFirst(); user.setAge(26);
}
});
修改也是需要在事务中操作。使用查询语句得到数据,然后将内容改了即可。
- 删除数据
(1)使用deleteFromRealm()
//先查找到数据
final RealmResults 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表的全部数据
- 异步任务注意点
最后在销毁Activity或Fragment时,要取消掉异步任务
@Override
protected void onDestroy() { super.onDestroy(); if (deleteTask!=null&&!addTask.isCancelled()){ deleteTask.cancel(); } }
- 其他常见操作
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);
- 数据库版本升级
当数据结构发生变化是,需要升级数据库。对于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 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
五、参考文章
https://blog.csdn.net/u013651026/article/details/79481615
https://www.jianshu.com/p/28912c2f31db
原文地址https://www.cnblogs.com/WUXIAOCHANG/p/10662998.html
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Android开发过程中的坑及解决方法收录(六)
Android开发过程中的坑及解决方法收录(六) file.listFiles 空指针异常最近在弄个小项目,类似一个文件管理器,需要获得手机存储里的目录之后显示,但是运行过程中出现错误,搜索了资料,得出了以下的解决办法 问题产生的原因: android6.0以上需要动态申请权限,确保自己有读取和写入SD卡的权限以往版本的话,写入SD卡的权限其实是包含了读取SD卡的权限,但是android6.0有所改变,所以得声明读取和写入SD卡的权限,两个权限存在有隐藏文件,在获取列表的时候出现空指针问题解决方法:针对第三个问题,我们只需要添加一个判断当前的数组是否为空即可 java.io.File[] childFiles = file.listFiles(new FileFilter() { @Override public boolean accept(java.io.File pathname) { return pathname.isDirectory() || pathname.getName().endsWith(".apk");//筛选文件夹和apk文件,统计为项目数 } }); if...
- 下一篇
复杂type页面封装库,支持多种状态切换和下拉刷新上拉加载
目录介绍 1.复杂页面库介绍 2.本库优势亮点 2.1 支持多种状态切换管理 2.2 支持添加多个header和footer 2.3 支持侧滑功能和拖拽移动 2.4 其他亮点介绍 3.如何使用介绍 3.1 最基础的使用 3.2 添加下拉刷新和加载更多监听 3.3 添加header和footer操作 3.4 设置数据和刷新 3.5 设置adapter 3.6 设置条目点击事件 3.7 设置侧滑删除功能[QQ侧滑删除] 3.8 轻量级拖拽排序与滑动删除 4.关于状态切换 4.1 关于布局内容 4.2 关于实现思路 4.3 关于状态切换api调用 4.4 关于自定义状态布局 4.5 关于自定义布局交互事件处理 5.常用api介绍 5.1 状态切换方法说明 5.2 viewHolder方法说明 5.3 adapter方法说明 5.4 分割线方法说明 5.5 swipe侧滑方法说明 5.6 其他api说明 6.优化实现点 6.1 viewHolder优化 6.2 状态管理器优化 6.3 recyclerView滑动卡顿优化 6.4 多线程下插入数据优化 6.5 rv四级缓存 6.6 异常情况下保...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS7设置SWAP分区,小内存服务器的救世主
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Hadoop3单机部署,实现最简伪集群
- CentOS关闭SELinux安全模块
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS8安装Docker,最新的服务器搭配容器使用