深入解析Sqlite的完美替代者,android数据库新王者——Realm
写在前面:
又到一年一度七夕虐狗节,看着大家忍受着各种朋友圈和QQ空间还有现实生活中的轮番轰炸,我实在不忍心再在这里给大家补刀,所以我觉得今天不虐狗,继续给大家分享有用的。
如果你比较关心android开发的最新动态的话,我想你一定知道android数据库新王者,没错,就是这个东西——Realm。
在安卓开发中,我们有sharedPreference和文件操作,当然还有一直为之自豪的轻量级数据库sqlite。
SharedPreference其实是采用xml的方式,以键值对形式存储基本数据类型的数据,对于复杂的数据筛选查询操作,file和sharedpreference就显得鸡肋,这个时候sqlite便可以满足有大量复杂查询要求的缓存数据操作,但是它的使用一直被人诟病,是的,因为代码量太多了!
作为一个要为全世界做贡献的程序猿,怎么可以忍受这样多的代码实现一点小东西呢?No,No,我们绝对不能接受!!!
还好网上出现了很多优秀的ORM框架供我们参略,比如ORMite,greenDao等,而这些都是基于SQLite的。
额,还是不扯远了,直接给大家带来今天的主角,没错,就是它——realm!
对于初学Realm的小伙伴,肯定还是应该对比着我们熟悉不过的sqlite来说的。
相比sqlite,Realm具有别人不可比拟的神奇速度,哦,这不是重点,它还有强大先进的特性等着你,比如数据加密支持,对Json的支持,流畅的API,数据观察者变化,所有的一切都是为了让我们程序猿更加潇洒!什么?程序猿还可以潇洒??有的小伙伴肯定怒火中烧了?楼主别装逗比。
额,好吧,只要你心潇洒,你人就潇洒啦~怎么跑题了呢?还是回归正题!!!
哦,对了,对于Realm,有一个非常强大的东西,就是它可以和当前最主流的网络框架Retrofit以及强大的异步框架Rxjava联用哦,对于Retrofit的介绍可以去直通车:
http://www.cnblogs.com/liushilin/p/5680135.html
等等,怎么总感觉少说了点什么,额,对,如标题一样,它还有一个很强大的特性,那就是它可以总能获取到最新的数据,是的,它是一个live db~~
额,还是说说怎么使用吧,更具体的可以看官方API频道:https://realm.io/docs/java/latest/
1)首先在你的project的gradle文件中添加这样一句,注意:是工程gradle,不是app的gradle,无法编译别怪楼主没提醒你哈!!
1 classpath 'io.realm:realm-gradle-plugin:1.1.0'
2)然后再去app下的gradle头部添加:
1 apply plugin: 'realm-android'
3)随便写一个Java实体类,例如我写了一个用户类User,只需要继承RealmObject即可,对于主键可以添加注解@PrimaryKey,对于@Required自然是必填项,@Ignore即是可忽略的。
1 package com.example.nanchen.realmdemo; 2 3 import io.realm.RealmObject; 4 import io.realm.annotations.Ignore; 5 import io.realm.annotations.PrimaryKey; 6 import io.realm.annotations.Required; 7 8 /** 9 * 10 * @author nanchen 11 * @date 2016-08-08 17:21:15 12 */ 13 public class User extends RealmObject { 14 //主键必须添加注解 15 @PrimaryKey 16 private int id;//主键id 17 @Required //注解设为Required代表必须项 18 private String name;//姓名 19 20 private int age;//年龄 21 22 @Ignore //表示忽视项,数据库不会存储该字段 23 private boolean hasGrilFriend;//是否有女朋友 24 25 public User() { 26 } 27 28 public User(int id, String name, int age) { 29 this.id = id; 30 this.name = name; 31 this.age = age; 32 } 33 34 public User(String name, int id, int age, boolean hasGrilFriend) { 35 this.name = name; 36 this.id = id; 37 this.age = age; 38 this.hasGrilFriend = hasGrilFriend; 39 } 40 41 public boolean isHasGrilFriend() { 42 return hasGrilFriend; 43 } 44 45 public void setHasGrilFriend(boolean hasGrilFriend) { 46 this.hasGrilFriend = hasGrilFriend; 47 } 48 49 public int getId() { 50 return id; 51 } 52 53 public void setId(int id) { 54 this.id = id; 55 } 56 57 public String getName() { 58 return name; 59 } 60 61 public void setName(String name) { 62 this.name = name; 63 } 64 65 public int getAge() { 66 return age; 67 } 68 69 public void setAge(int age) { 70 this.age = age; 71 } 72 73 @Override 74 public String toString() { 75 return "User{" + 76 "id=" + id + 77 ", name='" + name + '\'' + 78 ", age=" + age + 79 ", hasGrilFriend=" + hasGrilFriend + 80 '}'; 81 } 82 }
4)额,这个东西肯定也是需要初始化的嘛。额,楼主这里就单独写一个RealmUtils工具类了。
1 package com.example.nanchen.realmdemo; 2 3 import android.content.Context; 4 5 import io.realm.Realm; 6 import io.realm.RealmConfiguration; 7 8 /** 9 * @author nanchen 10 * @date 16-8-8 下午5:51 11 */ 12 public class RealmUtils { 13 private Context context; 14 private static RealmUtils mInstance; 15 private String realName = "myRealm.realm"; 16 17 private RealmUtils(Context context){ 18 this.context = context; 19 } 20 21 public static RealmUtils getInstance(Context context){ 22 if (mInstance == null){ 23 synchronized (RealmUtils.class){ 24 if (mInstance == null){ 25 mInstance = new RealmUtils(context); 26 } 27 } 28 } 29 return mInstance; 30 } 31 32 /** 33 * 获得Realm对象 34 * @return 35 */ 36 public Realm getRealm(){ 37 return Realm.getInstance(new RealmConfiguration.Builder(context).name(realName).build()); 38 } 39 }
5)然后在看看基本操作,楼主这里就使用比较流行的Dao模式吧
先写一个Dao接口,注释很清楚哈。
1 package com.example.nanchen.realmdemo; 2 3 import java.sql.SQLException; 4 import java.util.List; 5 6 /** 7 * 操作数据库的接口Dao 8 * 9 * @author nanchen 10 * @date 2016-08-08 17:23:18 11 * 12 */ 13 public interface UserDao { 14 15 /** 16 * 插入一个用户 17 * @param user 需要插入的用户对象 18 * @throws SQLException 19 */ 20 void insert(User user) throws SQLException; 21 22 /** 23 * 获得所有的用户列表 24 * @return 用户列表 25 * @throws SQLException 26 */ 27 List<User> getAllUser() throws SQLException; 28 29 /** 30 * 更新一个用户 31 * @param user 需要更新的用户类 32 * @return 更新后的对象 33 * @throws SQLException 34 */ 35 User updateUser(User user) throws SQLException; 36 37 /** 38 * 根据姓名修改新姓名 39 * @param name1 老名字 40 * @param name2 新名字 41 * @throws SQLException 42 */ 43 void updateUser(String name1,String name2) throws SQLException; 44 45 /** 46 * 根据id删除用户 47 * @param id 用户主键 48 * @throws SQLException 49 */ 50 void deleteUser(int id) throws SQLException; 51 52 /** 53 * 异步添加用户 54 * @param user 需要添加的用户对象 55 * @throws SQLException 56 */ 57 void insertUserAsync(User user) throws SQLException; 58 59 /** 60 * 按名字或者年龄查找第一个User 61 */ 62 User findByNameOrAge(String name1,int age1) throws SQLException; 63 64 /** 65 * 清楚所有 66 * @throws SQLException 67 */ 68 void deleteAll() throws SQLException; 69 70 /** 71 * 关闭事务 72 */ 73 void closeRealm(); 74 }
然后是我们的Dao实现类,同样是满满的注释,看楼主对你们这么用心,很感动有木有,想以身相许了有木有,额,楼主,不搞基!!!
不过你既然这么心存感激,就在文章右下角给楼主点个赞吧~~嘿嘿。
1 package com.example.nanchen.realmdemo; 2 3 import android.content.Context; 4 5 import java.sql.SQLException; 6 import java.util.List; 7 8 import io.realm.Realm; 9 import io.realm.Realm.Transaction; 10 import io.realm.RealmResults; 11 import io.realm.Sort; 12 13 /** 14 * @author nanchen 15 * @date 16-8-8 下午5:49 16 */ 17 public class UserDaoImpl implements UserDao { 18 19 private Context context; 20 private Realm mRealm; 21 22 public UserDaoImpl(Context context) { 23 mRealm = RealmUtils.getInstance(context).getRealm(); 24 } 25 26 /** 27 * 同步插入 28 * @param user 需要插入的用户对象 29 * @throws SQLException 30 */ 31 @Override 32 public void insert(User user) throws SQLException { 33 mRealm.beginTransaction();//必须先开启事务 34 User user1 = mRealm.copyToRealm(user);//把User对象复制到Realm 35 mRealm.commitTransaction();//提交事务 36 // mRealm.close();//必须关闭,不然会造成内存泄漏 37 } 38 39 /** 40 * 返回所有的User对象,并按照名字首字母排序 41 * @return User对象表 42 * @throws SQLException 43 */ 44 @Override 45 public List<User> getAllUser() throws SQLException { 46 List<User> list = null; 47 RealmResults<User> results = mRealm.where(User.class).findAll(); 48 results.sort("name", Sort.DESCENDING);//针对字符串的排序,但目前并不是支持所有字符集 49 list = results; 50 // mRealm.close(); 51 return list; 52 } 53 54 /** 55 * 更新一个User 56 * @param user 需要更新的用户类 57 * @return 返回更新后的User 58 * @throws SQLException 59 */ 60 @Override 61 public User updateUser(User user) throws SQLException { 62 mRealm.beginTransaction();//开启事务 63 User user1 = mRealm.copyToRealmOrUpdate(user); 64 mRealm.commitTransaction();//提交事务 65 // mRealm.close();//必须关闭事务 66 return user1; 67 } 68 69 /** 70 * @param name1 老名字 71 * @param name2 新名字 72 * @throws SQLException 73 */ 74 @Override 75 public void updateUser(String name1, String name2) throws SQLException { 76 mRealm.beginTransaction();//开启事务 77 mRealm.where(User.class) 78 .equalTo("name",name1)//查询出name为name1的User对象 79 .findFirst() 80 .setName(name2);//修改查询出的第一个对象的名字 81 mRealm.commitTransaction(); 82 // mRealm.close(); 83 } 84 85 /** 86 * 根据id删除一个User 87 * @param id 用户主键 88 * @throws SQLException 89 */ 90 @Override 91 public void deleteUser(int id) throws SQLException { 92 User user = mRealm.where(User.class).equalTo("id",id).findFirst();//删除id列值为id的行 93 mRealm.beginTransaction(); 94 user.deleteFromRealm();//从数据库删除 95 mRealm.commitTransaction(); 96 // mRealm.close(); 97 } 98 99 /** 100 * 异步插入User 101 * @param user 需要添加的用户对象 102 * @throws SQLException 103 */ 104 @Override 105 public void insertUserAsync(final User user) throws SQLException { 106 //一个Realm只能在同一个线程访问,在子线程中进行数据库操作必须重新获取realm对象 107 mRealm.executeTransaction(new Transaction() { 108 @Override 109 public void execute(Realm realm) { 110 realm.beginTransaction();//开启事务 111 User user1 = realm.copyToRealm(user); 112 realm.commitTransaction(); 113 realm.close();//记得关闭事务 114 } 115 }); 116 // mRealm.close();//外面也不能忘记关闭事务 117 } 118 119 120 /** 121 * 返回第一个指定名字或者年龄的对象 122 * @param name1 名字 123 * @param age1 年龄 124 */ 125 @Override 126 public User findByNameOrAge(String name1,int age1) throws SQLException{ 127 User user = mRealm.where(User.class) 128 .equalTo("name",name1)//相当于where name = name1 129 .or()//或,连接查询条件,没有这个方式时会默认是&连接 130 .equalTo("age",age1)//相当于where age = age1 131 .findFirst(); 132 //整体相当于select * from (表名) where name = (传入的name) or age = (传入的age)limit 1; 133 // mRealm.close(); 134 return user; 135 } 136 137 @Override 138 public void deleteAll() throws SQLException { 139 mRealm.beginTransaction(); 140 mRealm.where(User.class).findAll().deleteAllFromRealm(); 141 mRealm.commitTransaction(); 142 // mRealm.close(); 143 } 144 145 146 @Override 147 public void closeRealm() { 148 mRealm.close(); 149 } 150 }
6)实际上很多时候我们的close都是写在方法里面的,楼主只是为了测试Demo的好用,就单独写了一个关闭事务的方法来标新立异了哈,大家各自创新~~
额,对了,close你是必须必须调用的,不然你会内存泄漏!!
再简单看一下楼主的调用:
1 package com.example.nanchen.realmdemo; 2 3 import android.support.v7.app.AppCompatActivity; 4 import android.os.Bundle; 5 import android.util.Log; 6 7 import java.sql.SQLException; 8 9 public class MainActivity extends AppCompatActivity { 10 private UserDao userDao; 11 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 super.onCreate(savedInstanceState); 15 setContentView(R.layout.activity_main); 16 17 userDao = new UserDaoImpl(this); 18 try { 19 userDao.deleteAll();//先删除所有,以免demo出现主键已经存在的异常 20 User user = new User(); 21 user.setId(10); 22 user.setName("小刺猬"); 23 user.setAge(22); 24 user.setHasGrilFriend(true); 25 userDao.insert(user); 26 27 Log.d("flag","插入小刺猬----"+userDao.getAllUser().toString()); 28 29 for (int i = 0; i < 5; i++) { 30 userDao.insert(new User(i,"南尘"+i,20+i)); 31 } 32 Log.d("flag","插入5个对象----"+userDao.getAllUser().toString()); 33 Log.d("flag","查询1----"+userDao.findByNameOrAge("南尘1",20)); 34 Log.d("flag","查询2----"+userDao.findByNameOrAge("南尘1",23)); 35 userDao.updateUser("南尘1","nanchen"); 36 Log.d("flag","更新1----"+userDao.findByNameOrAge("南尘1",23)); 37 userDao.deleteUser(0);//删除0 38 Log.d("flag","删除后查看----"+userDao.getAllUser().toString()); 39 40 41 //统一关闭事务 42 userDao.closeRealm(); 43 } catch (SQLException e) { 44 e.printStackTrace(); 45 } 46 } 47 }
7)运行查看结果,好像没啥问题呢。
哈哈,对于Realm的简单使用今天就讲到这里哦,大家赶紧搞定一波学习,有女朋友的陪女朋友去,没女朋友的,赶紧学会了好找女朋友!
项目已同步至:https://github.com/nanchen2251/RealmDemo
额,最后根据Realm类总结一番:
1)Realm类可以对你的持久化对象进行存储和事务管理,可以用来创建RealmObjects实例,并且领域内的对象可以在任何时候查询和读取。
2)修改,插入和删除操作均必须在一个完整的事务中,在更新操作中,我们可以通过copyToRealmOrUpdate来做,但是官方更推荐我们用先查询出来后更新的方法,上面代码也有提到。
3)该事务确保多个实例(在多个线程中)可以在一个一致的状态和保证事务在ACID前提下,访问相同的对象。
4)当一个Realm实例操作完成后,一定一定要记住调用close()方法,否则导致了本地资源无法释放而引起了OOM别怪楼主没提醒。
5)Realm实例不能不在不同的线程间访问操作,所以楼主的异步插入里面打开了一个新的实例,当然也得关掉它!
6)对于UI线程来说。打开和关闭Realm实例,应当放在onCreate/onDestory或者onPause/onStop方法中。
7)在不同的线程间,Realm实例使用Handler机制来调整它的状态。也就是说,Realm实例在线程中,如果没有Looper,是不能收到更新通知的。除非手动调用waitForChange方法。
8)重点注意:Realm数据库的主键字段不是自动增长的,并且不支持设置数据的自增。需要自己设置,做添加的时候如果不给id字段值,默认为是0。后面再添加的话会报错说id为0的字段已经存在。尤其是批量添加的时候要注意,当心出现只添加了一条记录的悲剧!
9)数据自动更新。可以通过调用addChangeListener(context)来做。当数据库的数据有变化时,系统会自动回调此方法,说到这里,小伙伴是不是心动了?在列表数据的时候,麻麻再也不用担心我忘了更新数据库了。
注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接:http://www.cnblogs.com/liushilin/p/5752099.html
若您觉得这篇文章还不错请点击下右下角的推荐,非常感谢!
作 者:
南 尘
出 处: http://www.cnblogs.com/liushilin/
关于作者:专注于移动前端的项目开发。如有问题或建议,请多多赐教!欢迎加入Android交流群:118116509
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我
声援博主:如果您觉得文章对您有帮助,可以点击文章下部【推荐】或侧边【关注】。您的鼓励是作者坚持原创和持续写作的最大动力!
欢迎关注我的公众号,精讲面试、算法、Andrid、Java、Python,旨在打造全网最比心的公众号。