【Android】LitePal安裝和使用
本文基本上為整理稿。感謝LitePal的作者和郭霖大神。
參考文獻:
Github — https://github.com/LitePalFramework/LitePal
《LitePal 1.6.0版本來襲,數(shù)據(jù)加解密功能保障你的應(yīng)用數(shù)據(jù)安全》— https://mp.weixin.qq.com/s?__biz=MzA5MzI3NjE2MA%3D%3D&mid=2650240766&idx=1&sn=096f029aa531d5d99191c3d4c9126fc1#wechat_redirect
Tag:ORM,Android、LitePal
[TOC]
關(guān)于LitePal
LitePal是一個可以讓開發(fā)者更簡單操作SQLite的開源Android庫。你不用寫SQL命令就可以完成大部分諸如創(chuàng)建或更新表、CRUD操作和聚合函數(shù)等數(shù)據(jù)庫操作。LitePal的安裝也十分簡單,不用5分鐘就可以集成到你的項目中。
特性
- 使用對象關(guān)系映射(ORM)設(shè)計模式;
- 幾乎零配置(僅僅配置一個很少屬性的配置文件);
- 自動維護所有的表(比如:創(chuàng)建表、修改表和刪除表);
- 多數(shù)據(jù)庫支持;
- 為避免寫SQL語句而封裝了APIs;
- 極為流暢的查詢API;
- 仍然還可以選擇使用SQL,但APIs要比原生的更好更容易。
安裝
導入庫
在AndroidStudio中,編輯build.gradle文件,加入以下:
dependencies {
compile 'org.litepal.android:core:1.6.0'
}
配置litepal.xml
在項目中的assets文件下,創(chuàng)建并命名一個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>
將加密數(shù)據(jù)庫存儲在SD卡
如果需要將加密后的數(shù)據(jù)庫保存到SD卡上,則需要修改litepal.xml中的配置。代碼如下:
<litepal>
<storage value="wolf/database" />
</litepal>
注意:不需要填寫SD卡的完成路徑,需要配置相對路徑即可。
由于LitePal中既沒有Activity也沒有Fragment,所以LitePal是不會去幫你申請運行時的SD卡訪問讀寫權(quán)限。如果選擇將數(shù)據(jù)庫文件存儲在SD卡上,請一定要確保你的應(yīng)用程序已經(jīng)對訪問SD卡權(quán)限進行了運行時權(quán)限處理,否則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)。
使用
創(chuàng)建表
比如,有兩個模型類:【專輯】和【歌曲】。定義模型如下:
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>
這樣,當進行數(shù)據(jù)庫操作的時候,會自動生成表。例如:
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
);
更新表
例如添加一個發(fā)布時間字段并注釋掉【售價】字段:
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文件中的數(shù)據(jù)庫版本會自動進行更新。
<!--
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>
保存數(shù)據(jù)
保存操作的API是面向?qū)ο蟮?。每個繼承于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();
更新數(shù)據(jù)
最簡單的方法,通過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");
刪除數(shù)據(jù)
使用DataSupport中靜態(tài)方法delete()刪除一條記錄。
DataSupport.delete(Song.class, id);
或者使用DataSupport中靜態(tài)方法deleteAll()刪除多條記錄。
DataSupport.deleteAll(Song.class, "duration > ?" , "350");
查詢數(shù)據(jù)
從【歌曲】表中通過id查找單一條記錄。
Song song = DataSupport.find(Song.class, id);
從【歌曲】表中查找多條記錄。
List<Song> allSongs = DataSupport.findAll(Song.class);
通過fluent query構(gòu)建復雜查詢。
List<Song> songs = DataSupport.where("name like ?", "song%").order("duration").find(Song.class);
異步操作
默認每個數(shù)據(jù)庫操作都是在主線程上。如果操作可能花費很長的時間,例如保存或者查詢大量的記錄,可能需要使用異動操作。
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()方法,當異步操作完成時,通過回調(diào)onFinish()方法返回查詢結(jié)果。
相同地,異步存儲代碼如下:
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()方法,當在后臺將【專輯】存儲到數(shù)據(jù)庫后,通過回調(diào)onFinish()方法返回存儲結(jié)果。
多數(shù)據(jù)庫
如果App需要多個數(shù)據(jù)庫,LitePal也是完全支持的。在運行時你可以創(chuàng)建多個想要的數(shù)據(jù)庫。例如:
LitePalDB litePalDB = new LitePalDB("demo2", 1);
litePalDB.addClassName(Singer.class.getName());
litePalDB.addClassName(Album.class.getName());
litePalDB.addClassName(Song.class.getName());
LitePal.use(litePalDB);
上面這段代碼是創(chuàng)建一個demo2的數(shù)據(jù)庫,里面有【歌手】、【專輯】和【歌曲】三張表。
如果你只是想創(chuàng)建一個新的數(shù)據(jù)庫,而不想配置litepal.xml,你可以參照如下:
LitePalDB litePalDB = LitePalDB.fromDefault("newdb");
LitePal.use(litePalDB);
你可以通過下面的操作切換回默認數(shù)據(jù)庫。
LitePal.useDefault();
你也可以通過表名刪除任何數(shù)據(jù)庫。
LitePal.deleteDatabase("newdb");
字符串加密
從1.6.0版本LitePal內(nèi)置了對數(shù)據(jù)(字符串)進行AES或者MD5加解密的功能。
AES:全稱是Advanced Encryption Standard,中文名叫高級加密標準,同時它也是美國聯(lián)邦政府采用的一種區(qū)塊加密標準。
MD5:全稱是Message Digest Algorithm 5,中文名叫信息摘要算法第五版。要說到MD5加密算法的特點其實有很多很多,但是它最為突出的一個特點就是,使用這種加密算法計算出來的結(jié)果是不可逆的。通俗點來說,就是MD5算法只能進行加密但不能進行解密。
AES
一個書本類,類中有一個【書名】屬性和一個【頁數(shù)】屬性,現(xiàn)在將【書名】屬性的值進行加密,只需要在【書名】屬性的上方加上@Encrypt(algorithm = AES)這樣一行注解即可代碼如下:
public class Book extends DataSupport {
@Encrypt(algorithm = AES)
private String name;
private int page;
// getter and setter
}
對于開發(fā)者而言,加解密操作是完全透明化的。也就是說,作為開發(fā)者并不用考慮某個字段有沒有被加密,然后要不要進行解密等等,我們只需要仍然使用標準的LitePal API來查詢數(shù)據(jù)即可。
比如從書本表中查詢這條數(shù)據(jù),并打印。
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);
細節(jié)
可以自定義AES算法的密鑰。如果沒有指定密鑰,LitePal會使用默認的密鑰進行加解密。使用
LitePal.Key()方法來自定義密鑰;AES算法和MD5算法都只對
String類型的字段有效,如果你嘗試給其他類型的字段(比如說int字段)指定@Encrypt注解,LitePal并不會執(zhí)行任何加密操作;-
加密后的數(shù)據(jù)字段不能再通過
where語句來進行查詢、修改或刪除。?
也就是說,執(zhí)行類似于 where("name = ?", "第一行代碼") 這樣的語句將無法查到任何數(shù)據(jù),因為在數(shù)據(jù)庫中存儲的真實值已經(jīng)不是"第一行代碼"了。
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是一個壓縮、優(yōu)化和混淆Java字節(jié)碼文件的免費的工具,它可以刪除無用的類、字段、方法和屬性??梢詣h除沒用的注釋,最大限度地優(yōu)化字節(jié)碼文件。它還可以使用簡短的無意義的名稱來重命名已經(jīng)存在的類、字段、方法和屬性。常常用于Android開發(fā)用于混淆最終的項目,增加項目被反編譯的難度。