【Android】LitePal安装和使用
本文基本上为整理稿。感谢LitePal的作者和郭霖大神。
参考文献:
Github — https://github.com/LitePalFramework/LitePal
《LitePal 1.6.0版本来袭,数据加解密功能保障你的应用数据安全》— https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA%3D%3D&mid=2650240766&idx=1&sn=096f029aa531d5d99191c3d4c9126fc1#wechat_redirect
Tag:ORM
,Android
、LitePal
[TOC]
关于LitePal
LitePal是一个可以让开发者更简单操作SQLite的开源Android库。你不用写SQL命令就可以完成大部分诸如创建或更新表、CRUD操作和聚合函数等数据库操作。LitePal的安装也十分简单,不用5分钟就可以集成到你的项目中。
特性
- 使用对象关系映射(ORM)设计模式;
- 几乎零配置(仅仅配置一个很少属性的配置文件);
- 自动维护所有的表(比如:创建表、修改表和删除表);
- 多数据库支持;
- 为避免写SQL语句而封装了APIs;
- 极为流畅的查询API;
- 仍然还可以选择使用SQL,但APIs要比原生的更好更容易。
安装
导入库
在AndroidStudio中,编辑build.gradle
文件,加入以下:
dependencies {
compile 'org.litepal.android:core:1.6.0'
}
配置litepal.xml
在项目中的assets
文件下,创建并命名一个litepal.xml
文件,并编辑修改为以下代码:
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<!--
Define the database name of your application.
By default each database name should be end with .db.
If you didn't name your database end with .db,
LitePal would plus the suffix automatically for you.
For example:
<dbname value="demo" />
-->
<dbname value="demo" />
<!--
Define the version of your database. Each time you want
to upgrade your database, the version tag would helps.
Modify the models you defined in the mapping tag, and just
make the version value plus one, the upgrade of database
will be processed automatically without concern.
For example:
<version value="1" />
-->
<version value="1" />
<!--
Define your models in the list with mapping tag, LitePal will
create tables for each mapping class. The supported fields
defined in models will be mapped into columns.
For example:
<list>
<mapping class="com.test.model.Reader" />
<mapping class="com.test.model.Magazine" />
</list>
-->
<list>
</list>
<!--
Define where the .db file should be. "internal" means the .db file
will be stored in the database folder of internal storage which no
one can access. "external" means the .db file will be stored in the
path to the directory on the primary external storage device where
the application can place persistent files it owns which everyone
can access. "internal" will act as default.
For example:
<storage value="external" />
<storage value="wolf/database" />
-->
</litepal>
将加密数据库存储在SD卡
如果需要将加密后的数据库保存到SD卡上,则需要修改litepal.xml
中的配置。代码如下:
<litepal>
<storage value="wolf/database" />
</litepal>
注意:不需要填写SD卡的完成路径,需要配置相对路径即可。
由于LitePal中既没有Activity也没有Fragment,所以LitePal是不会去帮你申请运行时的SD卡访问读写权限。如果选择将数据库文件存储在SD卡上,请一定要确保你的应用程序已经对访问SD卡权限进行了运行时权限处理,否则LitePal的所有操作都将会失败。
配置LitePalApplication
配置AndroidManifest.xml
如下:
<manifest>
<application
android:name="org.litepal.LitePalApplication">
</application>
</manifest>
当然,还有一种配置方法。
<manifest>
<application
android:name="com.example.MyOwnApplication">
</application>
</manifest>
public class MyOwnApplication extends xxxApplication {
@Override
public void onCreate() {
super.onCreate();
LitePal.initialize(this);
}
}
注意:最好是在
onCreate()
方法中进行初始化LitePal.initialize(this)
。
使用
创建表
比如,有两个模型类:【专辑】和【歌曲】。定义模型如下:
public class Album extends DataSupport {
@Column(unique = true, defaultValue = "unknown")
private String name;
private float price;
private byte[] cover;
private List<Song> songs = new ArrayList<Song>();
// generated getters and setters.
}
public class Song extends DataSupport {
@Column(nullable = false)
private String name;
private int duration;
@Column(ignore = true)
private String uselessField;
private Album album;
// generated getters and setters.
}
然后,在litepal.xml
文件中添加这两个模型的映射列表。
<list>
<mapping class="org.litepal.litepalsample.model.Album" />
<mapping class="org.litepal.litepalsample.model.Song" />
</list>
这样,当进行数据库操作的时候,会自动生成表。例如:
SQLiteDatabase db = LitePal.getDatabase();
自动生成的表,等价于以下SQLs:
CREATE TABLE album (
id integer primary key autoincrement,
name text unique default 'unknown',
price real,
cover blob
);
CREATE TABLE song (
id integer primary key autoincrement,
name text not null,
duration integer,
album_id integer
);
更新表
例如添加一个发布时间字段并注释掉【售价】字段:
public class Album extends DataSupport {
@Column(unique = true, defaultValue = "unknown")
private String name;
@Column(ignore = true)
private float price;
private byte[] cover;
private Date releaseDate;
private List<Song> songs = new ArrayList<Song>();
// generated getters and setters.
}
litepal.xml
文件中的数据库版本会自动进行更新。
<!--
Define the version of your database. Each time you want
to upgrade your database, the version tag would helps.
Modify the models you defined in the mapping tag, and just
make the version value plus one, the upgrade of database
will be processed automatically without concern.
For example:
<version value="1" ></version>
-->
<version value="2" ></version>
保存数据
保存操作的API是面向对象的。每个继承于DataSupport
的模型都有save()
方法。例如:
Album album = new Album();
album.setName("album");
album.setPrice(10.99f);
album.setCover(getCoverImageBytes());
album.save();
Song song1 = new Song();
song1.setName("song1");
song1.setDuration(320);
song1.setAlbum(album);
song1.save();
Song song2 = new Song();
song2.setName("song2");
song2.setDuration(356);
song2.setAlbum(album);
song2.save();
更新数据
最简单的方法,通过find()
找到记录,并使用save()
方法更新记录。
Album albumToUpdate = DataSupport.find(Album.class, 1);
albumToUpdate.setPrice(20.99f); // raise the price
albumToUpdate.save();
每个继承于DataSupport
的模型,都有update()
和updateAll()
方法。
Album albumToUpdate = new Album();
albumToUpdate.setPrice(20.99f); // raise the price
albumToUpdate.update(id);
或者带有where
条件的更新多条记录。
Album albumToUpdate = new Album();
albumToUpdate.setPrice(20.99f); // raise the price
albumToUpdate.updateAll("name = ?", "album");
删除数据
使用DataSupport
中静态方法delete()
删除一条记录。
DataSupport.delete(Song.class, id);
或者使用DataSupport
中静态方法deleteAll()
删除多条记录。
DataSupport.deleteAll(Song.class, "duration > ?" , "350");
查询数据
从【歌曲】表中通过id
查找单一条记录。
Song song = DataSupport.find(Song.class, id);
从【歌曲】表中查找多条记录。
List<Song> allSongs = DataSupport.findAll(Song.class);
通过fluent query
构建复杂查询。
List<Song> songs = DataSupport.where("name like ?", "song%").order("duration").find(Song.class);
异步操作
默认每个数据库操作都是在主线程上。如果操作可能花费很长的时间,例如保存或者查询大量的记录,可能需要使用异动操作。
LitePal的所有CRUD方法都支持异步操作。例如,在后台线程从【歌曲】表中查找多条记录,代码如下:
DataSupport.findAllAsync(Song.class).listen(new FindMultiCallback() {
@Override
public <T> void onFinish(List<T> t) {
List<Song> allSongs = (List<Song>) t;
}
});
使用findAllAsync()
代替findAll()
,并且拓展一个listen()
方法,当异步操作完成时,通过回调onFinish()
方法返回查询结果。
相同地,异步存储代码如下:
Album album = new Album();
album.setName("album");
album.setPrice(10.99f);
album.setCover(getCoverImageBytes());
album.saveAsync().listen(new SaveCallback() {
@Override
public void onFinish(boolean success) {
}
});
使用saveAsync()
代替save()
,并且拓展一个listen()
方法,当在后台将【专辑】存储到数据库后,通过回调onFinish()
方法返回存储结果。
多数据库
如果App需要多个数据库,LitePal也是完全支持的。在运行时你可以创建多个想要的数据库。例如:
LitePalDB litePalDB = new LitePalDB("demo2", 1);
litePalDB.addClassName(Singer.class.getName());
litePalDB.addClassName(Album.class.getName());
litePalDB.addClassName(Song.class.getName());
LitePal.use(litePalDB);
上面这段代码是创建一个demo2
的数据库,里面有【歌手】、【专辑】和【歌曲】三张表。
如果你只是想创建一个新的数据库,而不想配置litepal.xml
,你可以参照如下:
LitePalDB litePalDB = LitePalDB.fromDefault("newdb");
LitePal.use(litePalDB);
你可以通过下面的操作切换回默认数据库。
LitePal.useDefault();
你也可以通过表名删除任何数据库。
LitePal.deleteDatabase("newdb");
字符串加密
从1.6.0版本LitePal内置了对数据(字符串)进行AES或者MD5加解密的功能。
AES:全称是Advanced Encryption Standard,中文名叫高级加密标准,同时它也是美国联邦政府采用的一种区块加密标准。
MD5:全称是Message Digest Algorithm 5,中文名叫信息摘要算法第五版。要说到MD5加密算法的特点其实有很多很多,但是它最为突出的一个特点就是,使用这种加密算法计算出来的结果是不可逆的。通俗点来说,就是MD5算法只能进行加密但不能进行解密。
AES
一个书本类,类中有一个【书名】属性和一个【页数】属性,现在将【书名】属性的值进行加密,只需要在【书名】属性的上方加上@Encrypt(algorithm = AES)
这样一行注解即可代码如下:
public class Book extends DataSupport {
@Encrypt(algorithm = AES)
private String name;
private int page;
// getter and setter
}
对于开发者而言,加解密操作是完全透明化的。也就是说,作为开发者并不用考虑某个字段有没有被加密,然后要不要进行解密等等,我们只需要仍然使用标准的LitePal API
来查询数据即可。
比如从书本表中查询这条数据,并打印。
Book book = DataSupport.findFirst(Book.class);
String name = book.getName();
int page = book.getPage();
Log.d(TAG, "book name is " + name);
Log.d(TAG, "book page is " + page);
细节
可以自定义AES算法的密钥。如果没有指定密钥,LitePal会使用默认的密钥进行加解密。使用
LitePal.Key()
方法来自定义密钥;AES算法和MD5算法都只对
String
类型的字段有效,如果你尝试给其他类型的字段(比如说int
字段)指定@Encrypt
注解,LitePal并不会执行任何加密操作;-
加密后的数据字段不能再通过
where
语句来进行查询、修改或删除。
也就是说,执行类似于 where("name = ?", "第一行代码") 这样的语句将无法查到任何数据,因为在数据库中存储的真实值已经不是"第一行代码"了。
MD5
与AES类似,加密基本是一模一样的用法,我们只需要将@Encrypt
中指定的加密算法改成MD5即可。
public class User extends DataSupport {
@Encrypt(algorithm = MD5)
private String password;
private String username;
// getter and setter
}
代码混淆
如果你需要使用Proguard,可能需要添加以下代码到项目文件中。
-keep class org.litepal.** {
*;
}
-keep class * extends org.litepal.crud.DataSupport {
*;
}
ProGuard是一个压缩、优化和混淆Java字节码文件的免费的工具,它可以删除无用的类、字段、方法和属性。可以删除没用的注释,最大限度地优化字节码文件。它还可以使用简短的无意义的名称来重命名已经存在的类、字段、方法和属性。常常用于Android开发用于混淆最终的项目,增加项目被反编译的难度。