Android本地?cái)?shù)據(jù)存儲(chǔ)Room實(shí)踐和優(yōu)化技巧
Room在SQLite基礎(chǔ)上做了ORM封裝,使用起來(lái)類(lèi)似JPA,不需要寫(xiě)太多的sql。
導(dǎo)入依賴(lài)
//room
def room_version="2.4.2"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
//implementation "androidx.room:room-rxjava2:$room_version"
//implementation "androidx.room:room-rxjava3:$room_version"
//implementation "androidx.room:room-guava:$room_version"
//testImplementation "androidx.room:room-testing:$room_version"
//implementation "androidx.room:room-paging:2.5.0-alpha01"
關(guān)鍵注解說(shuō)明
1、@Database:Room數(shù)據(jù)庫(kù)對(duì)象。該類(lèi)需要繼承自RoomDatabase,通過(guò)Room.databaseBuilder()結(jié)合單例設(shè)計(jì)模式,完成數(shù)據(jù)庫(kù)的創(chuàng)建工作。我們創(chuàng)建的Dao對(duì)象,在這里以抽象方法的形式返回,只需一行代碼即可。
- entities:指定該數(shù)據(jù)庫(kù)有哪些表
- version:指定數(shù)據(jù)庫(kù)版本號(hào),后續(xù)數(shù)據(jù)庫(kù)的升級(jí)正是依據(jù)版本號(hào)來(lái)判斷的
2、@Entity:該類(lèi)與Room中表關(guān)聯(lián)起來(lái)。tableName屬性可以為該表設(shè)置名字,如果不設(shè)置,則表名與類(lèi)名相同。
3、@PrimaryKey:用于指定該字段作為表的主鍵。
4、@ColumnInfo:設(shè)置該字段存儲(chǔ)在數(shù)據(jù)庫(kù)表中的名字并指定字段的類(lèi)型;默認(rèn)字段名和屬性名一樣
5、@Ignore:忽略該字段
一、使用步驟
1、創(chuàng)建實(shí)體類(lèi),對(duì)應(yīng)數(shù)據(jù)庫(kù)中一張表,使用注解@Entity
2、創(chuàng)建Dao接口類(lèi),用于操作數(shù)據(jù),使用注解@Dao;不需要實(shí)現(xiàn),在編譯的時(shí)候,框架會(huì)自動(dòng)生成實(shí)現(xiàn)類(lèi)
3、創(chuàng)建數(shù)據(jù)庫(kù)對(duì)象Database,繼承RoomDatabase,使用單例模式返回實(shí)例
4、在Activity中使用,Room數(shù)據(jù)操作必須在異步線(xiàn)程中執(zhí)行,所以在Activity中使用線(xiàn)程池執(zhí)行,或者使用RxJava切換線(xiàn)程
使用代碼示例
1、創(chuàng)建實(shí)體類(lèi),對(duì)應(yīng)數(shù)據(jù)庫(kù)中一張表,使用注解@Entity
@Entity
public class Person {
// 主鍵,自增長(zhǎng)
@PrimaryKey(autoGenerate = true)
private int id;
private String name;
private String sex;
private int age;
}
2、創(chuàng)建Dao接口類(lèi),用于操作數(shù)據(jù),使用注解@Dao;不需要實(shí)現(xiàn),在編譯的時(shí)候,框架會(huì)自動(dòng)生成實(shí)現(xiàn)類(lèi)
@Dao
public interface PersonDao {
// 插入
@Insert
void insertPersons(Person... persons);
// 修改
@Update
void updatePersons(Person... persons);
// 刪除所有
@Query("delete from Person")
void deleteAllPersons();
// 刪除指定實(shí)體
@Delete
void deletePersons(Person... persons);
// 根據(jù)id刪除
@Query("delete from Person where id in (:ids)")
void deleteByIds(int ...ids);
// 根據(jù)id查詢(xún)
@Query("select * from Person where id in (:ids)")
List<Person> selectByIds(int ...ids);
// 查詢(xún)所有
@Query("select * from Person order by id desc")
List<Person> selectAllPersons();
}
3、創(chuàng)建數(shù)據(jù)庫(kù)對(duì)象Database,繼承RoomDatabase,使用單例模式返回實(shí)例
@Database(entities = {Person.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract PersonDao personDao();
private volatile static AppDatabase instance;
public static AppDatabase getInstance(Context context){
if (instance == null) {
synchronized (DBHelper.class) {
if (instance == null) {
instance = Room.databaseBuilder(context, AppDatabase.class, "person.db").build();
}
}
}
return instance;
}
}
4、在Activity中使用
Room數(shù)據(jù)操作必須在異步線(xiàn)程中執(zhí)行,所以在Activity中使用線(xiàn)程池執(zhí)行
ExecutorService pool = Executors.newCachedThreadPool();
// 插入數(shù)據(jù)
public void insertRoom(View view) {
AppDatabase db = AppDatabase.getInstance(getApplicationContext());
pool.execute(() -> {
PersonDao dao = db.personDao();
Person p1 = new Person("用戶(hù)1", "男", 18);
Person p2 = new Person("用戶(hù)2", "男", 28);
Person p3 = new Person("用戶(hù)3", "男", 38);
dao.insertPersons(p1, p2, p3);
});
}
// 查詢(xún)數(shù)據(jù)
public void queryRoom(View view) {
AppDatabase db = AppDatabase.getInstance(getApplicationContext());
pool.execute(() -> {
PersonDao dao = db.personDao();
List<Person> list = dao.selectAllPersons();
list.forEach(p-> Log.d("test", p.toString()));
});
}
// 根據(jù)id查詢(xún)
public void queryRoomById(View view) {
AppDatabase db = AppDatabase.getInstance(getApplicationContext());
pool.execute(() -> {
PersonDao dao = db.personDao();
List<Person> list = dao.selectByIds(3,4);
list.forEach(p-> Log.d("test", p.toString()));
});
}
// 刪除
public void deleteRoom(View view) {
AppDatabase db = AppDatabase.getInstance(getApplicationContext());
pool.execute(() -> {
PersonDao dao = db.personDao();
dao.deleteByIds(1,2);
});
}
二、類(lèi)型轉(zhuǎn)換器
SQLite支持null,integer,real,text,blob五種數(shù)據(jù)類(lèi)型,實(shí)際上SQLite也接受varchar,char,decimal等數(shù)據(jù)類(lèi)型,只不過(guò)在運(yùn)算中或保存時(shí)會(huì)轉(zhuǎn)換成對(duì)應(yīng)的5種數(shù)據(jù)類(lèi)型,因此,可以將各種類(lèi)型數(shù)據(jù)保存到任何字段中。
除了上述基本類(lèi)型外,其他如Date、BigDecimal、或Json對(duì)象等如何存儲(chǔ)呢?
Room給我們提供的非常方便的類(lèi)型轉(zhuǎn)換器功能。
- @TypeConverter,定義類(lèi)型轉(zhuǎn)換靜態(tài)方法
- @TypeConverters,定義包含一組轉(zhuǎn)換方法的class類(lèi)
1、創(chuàng)建類(lèi)型轉(zhuǎn)換類(lèi)型,如,Date和Long互轉(zhuǎn)
使用注解@TypeConverter聲明具體的轉(zhuǎn)換方法,每個(gè)方法必須包含一個(gè)參數(shù),以及必須有返回值。
public class DateConverter {
@TypeConverter
public static Date toDate(Long dateLong) {
return dateLong == null ? null : new Date(dateLong);
}
@TypeConverter
public static Long fromDate(Date date) {
return date == null ? null : date.getTime();
}
}
2、將創(chuàng)建好的轉(zhuǎn)換器類(lèi),在entity上使用
使用注解@TypeConverters({DateConverter.class}),那么實(shí)體類(lèi)中的所有的Date屬性都會(huì)被轉(zhuǎn)換成Long存儲(chǔ),查詢(xún)?nèi)〕龅臅r(shí)候,會(huì)自動(dòng)從Long轉(zhuǎn)換成Date顯示。
注意:@TypeConverters放在元素屬性、Class、Dao、Database上面
- 放在元素屬性,只對(duì)改屬性有效
- 放在實(shí)體Class上,對(duì)class中所有元素有效
- 放在Dao上,對(duì)Dao的所有方法有效
- 放在Database,對(duì)Database的所有實(shí)體和所有Dao都有效
為避免出現(xiàn)混亂,通常建議只在Entity或?qū)傩陨隙x轉(zhuǎn)換器
@Entity
@TypeConverters({DateConverter.class})
public class BsGoods {
private static final long serialVersionUID = 1122172437556010779L;
// 主鍵
@PrimaryKey
private Long id;
private Date createdDate;
private Date updatedDate;
...
}
其他類(lèi)型轉(zhuǎn)換示例,BigDecimal轉(zhuǎn)String。
如果是JavaBean等復(fù)雜對(duì)象,可以轉(zhuǎn)換成Json字符串存儲(chǔ)。
public class BigDecimalConverter {
@TypeConverter
public static String toStr(BigDecimal decimal) {
return decimal == null ? null : decimal.toString();
}
@TypeConverter
public static BigDecimal toDecimal(String str) {
return str == null ? null : new BigDecimal(str);
}
}
三、結(jié)合RxJava
在Activity中使用,并且更新界面UI元素
Android的界面UI元素更新,必須在主線(xiàn)程中執(zhí)行,但是Room的數(shù)據(jù)查詢(xún),又只能使用異常線(xiàn)程處理。那么如何將查詢(xún)到數(shù)據(jù),更新到頁(yè)面控件上面呢?
這里可以結(jié)合RxJava實(shí)現(xiàn)流式操作,線(xiàn)下切換!
示例代碼,查詢(xún)所有商品數(shù)據(jù),顯示在頁(yè)面控件上面,控件使用的是自定義的TableView,暫不展開(kāi),這里只顯示數(shù)據(jù)查詢(xún)以及顯示。
1、在Database類(lèi)中定義查詢(xún)方法,傳入回調(diào)函數(shù)
public void selectAll(Consumer<List<BsGoods>> fun) {
BsGoodsDao dao = bsGoodsDao();
Observable.just("select")
.map(s -> dao.selectAll())
.subscribeOn(Schedulers.io())// 給上面的操作分配異步線(xiàn)程
.observeOn(AndroidSchedulers.mainThread())// 給終點(diǎn)分配安卓主線(xiàn)程
.subscribe(new Observer<List<BsGoods>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull List<BsGoods> bsGoods) {
fun.accept(bsGoods);
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
}
2、在Activity中使用,傳入回調(diào)函數(shù)更新界面UI
private void initializeTableViewLocal() {
BsGoodsDatabase db = BsGoodsDatabase.getInstance(getContext());
db.selectAll(list -> {
GoodsTableViewModel tableViewModel = new GoodsTableViewModel(list);
TableViewAdapter tableViewAdapter = new TableViewAdapter(tableViewModel);
mTableView.setAdapter(tableViewAdapter);
mTableView.setTableViewListener(new TableViewListener(mTableView));
tableViewAdapter.setAllItems(tableViewModel.getColumnHeaderList(), tableViewModel
.getRowHeaderList(), tableViewModel.getCellList());
});
}
到此這篇關(guān)于Android本地?cái)?shù)據(jù)存儲(chǔ)Room實(shí)踐和優(yōu)化技巧的文章就介紹到這了,更多相關(guān)Android數(shù)據(jù)存儲(chǔ)Room內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 出現(xiàn)問(wèn)題 Gradle
這篇文章主要介紹了Android 出現(xiàn)問(wèn)題 Gradle "xxx" project refresh failed解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04
Android 7.0以上版本實(shí)現(xiàn)應(yīng)用內(nèi)語(yǔ)言切換的方法
本篇文章主要介紹了Android 7.0以上版本實(shí)現(xiàn)應(yīng)用內(nèi)語(yǔ)言切換的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
詳解Android應(yīng)用開(kāi)發(fā)中Scroller類(lèi)的屏幕滑動(dòng)功能運(yùn)用
這篇文章主要介紹了詳解Android應(yīng)用開(kāi)發(fā)中Scroller類(lèi)的屏幕滑動(dòng)功能運(yùn)用,文中包括各種觸摸滑屏手勢(shì)相關(guān)方法的示例,需要的朋友可以參考下2016-02-02
Android自定義view實(shí)現(xiàn)列表內(nèi)左滑刪除Item
這篇文章主要介紹了微信小程序列表中item左滑刪除功能,本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02
android輸入框內(nèi)容改變的監(jiān)聽(tīng)事件實(shí)例
下面小編就為大家分享一篇android輸入框內(nèi)容改變的監(jiān)聽(tīng)事件實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
在Flutter中制作翻轉(zhuǎn)卡片動(dòng)畫(huà)的完整實(shí)例代碼
最近Flutter的勢(shì)頭是越來(lái)越猛了,作為一個(gè)Android程序猿,我自然也是想要趕緊嘗試一把,這篇文章主要給大家介紹了關(guān)于在Flutter中制作翻轉(zhuǎn)卡片動(dòng)畫(huà)的相關(guān)資料,需要的朋友可以參考下2021-10-10

