一、Room數(shù)據(jù)庫(kù)的大三組件
Database:即數(shù)據(jù)庫(kù)芳杏。擴(kuò)展了RoomDatabase的抽象類(lèi)矩屁。可以通過(guò)Room獲得它的一個(gè)實(shí)例爵赵。databaseBuilder或Room.inMemoryDatabaseBuilder吝秕。
Dao:數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象,是Room的主要組件空幻,負(fù)責(zé)定義訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的方法烁峭。
Entity:實(shí)體類(lèi),代表一個(gè)表結(jié)構(gòu)秕铛。
二约郁、實(shí)現(xiàn)原理
Room數(shù)據(jù)庫(kù)實(shí)現(xiàn)原理
在數(shù)據(jù)庫(kù)DataBase中想要訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的操作,需要獲取操作Dao的對(duì)象但两,而Dao會(huì)給DataBase返回一個(gè)entity的對(duì)象鬓梅。當(dāng)我們用Room數(shù)據(jù)庫(kù)進(jìn)行存儲(chǔ)時(shí),其實(shí)存儲(chǔ)的并非要存儲(chǔ)的具體對(duì)象镜遣,而是對(duì)象對(duì)應(yīng)的一些信息己肮。因此士袄,我們引入Repository倉(cāng)庫(kù)類(lèi)用于操作Room,而我們給外部暴露的是viewModel類(lèi)谎僻,因此娄柳,viewModel需要持有Repository對(duì)象。此外,Repository與viewModel之間也需要context傳遞上下文信息艘绍。
三赤拒、優(yōu)點(diǎn)
- 針對(duì) SQL 查詢(xún)的編譯時(shí)驗(yàn)證
- 可最大限度減少重復(fù)和容易出錯(cuò)的樣板代碼的方便注解
- 簡(jiǎn)化了數(shù)據(jù)庫(kù)遷移路徑
四、使用方式(以創(chuàng)建一個(gè)相冊(cè)數(shù)據(jù)庫(kù)為例)
- 聲明依賴(lài)項(xiàng)
def room_version = "2.3.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
- 創(chuàng)建相冊(cè)表實(shí)體類(lèi)Entity
import androidx.room.Entity
import androidx.room.PrimaryKey
const val ALBUM_TYPE_IMAGE = 0
const val ALBUM_TYPE_VIDEO = 1
//默認(rèn)表名就是類(lèi)名
//@Entity(tableName = "album_table")
@Entity
data class Album (
@PrimaryKey(autoGenerate = true)//主鍵自增長(zhǎng)取消诱鞠,因?yàn)槭莝tring類(lèi)型
val id:Int,
var albumName:String,
//@ColumnInfo(name ="cover_url") 表名和屬性名的轉(zhuǎn)換
var coverUrl:String,
var number:Int,
val type:Int = ALBUM_TYPE_IMAGE
)
//縮略圖表
@Entity
data class ThumbImage(
@PrimaryKey(autoGenerate = false)
val imageName:String,
//外鍵
val albumId: Int
)
- 創(chuàng)建Dao
import androidx.room.*
import java.util.concurrent.Flow
@Dao
interface AlbumDao {
//創(chuàng)建相冊(cè) 數(shù)據(jù)庫(kù)操作都耗時(shí)suspend
//@Insert(onConflict = OnConflictStrategy.)解決沖突
@Insert
fun insertAlbum(album: Album)
//刪除一個(gè)相冊(cè)
@Delete
fun deleteAlbum(album: Album)
//刪除多個(gè)相冊(cè) 可變數(shù)組
@Delete
fun deleteAlbums(vararg album: Album)
//更新相冊(cè)
@Update
fun updateAlbum(album: Album)
//查詢(xún)所有相冊(cè)信息
@Query("select * from Album where type = :type")
fun getAllAlbumsWithType(type:Int):kotlinx.coroutines.flow.Flow<List<Album>>
/**------------------------相片--------------------------**/
//插入一張圖
@Insert
fun insertImage(thumbImage: ThumbImage)
//插入多張圖
@Insert
fun insertImages(vararg thumbImage: ThumbImage)
//刪除一張圖
@Delete
fun deleteImage(thumbImage: ThumbImage)
//刪除多張圖
@Delete
fun deleteImages(vararg thumbImage: ThumbImage)
}
- 創(chuàng)建數(shù)據(jù)庫(kù)DataBase
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(
entities = [Album::class,ThumbImage::class],
version = 1,
exportSchema = false //是否導(dǎo)出
)
abstract class AlbumDatabase:RoomDatabase() {
//獲取一系列數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)的接口實(shí)現(xiàn)類(lèi)
abstract fun albumDao():AlbumDao
//訪(fǎng)問(wèn)靜態(tài)屬性挎挖,提供單例對(duì)象
//有就返回,沒(méi)有就加鎖再返回
companion object{
private var INSTANCE:AlbumDatabase ? = null
fun getInstance(context: Context):AlbumDatabase{
if (INSTANCE != null){
return INSTANCE!!
}
synchronized(this){
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(
context,
AlbumDatabase::class.java,
"album_db"
).build()
}
return INSTANCE!!
}
}
}
}
5.創(chuàng)建Repository給viewModel提供訪(fǎng)問(wèn)方法
import android.content.Context
import kotlinx.coroutines.flow.Flow
class Repository(context: Context) {
private var albumDao:AlbumDao
init {
albumDao = AlbumDatabase.getInstance(context).albumDao()
}
//加載相冊(cè)
suspend fun loadAlbumWithType(type:Int): Flow<List<Album>> {
return albumDao.getAllAlbumsWithType(type)
}
//插入相冊(cè)
suspend fun addAlbum(album: Album){
albumDao.insertAlbum(album)
}
}
- viewModel持有Repository航夺,給外部提供操作方法
package com.example.privatealbum.db
import android.app.Application
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.example.privatealbum.DEFAULT_COVER_URL
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
//與viewModel的區(qū)別是強(qiáng)制傳一個(gè)參數(shù),擁有application即擁有context
class SharedViewModel(application: Application)
:AndroidViewModel(application){
//保存所有相冊(cè)信息
var imageAlbumList = MutableLiveData<List<Album>>(emptyList())
var videoAlbumList = MutableLiveData<List<Album>>(emptyList())
//倉(cāng)庫(kù)對(duì)象
val repository = Repository(application.applicationContext)
//保存當(dāng)前添加相冊(cè)的類(lèi)型
var type = ALBUM_TYPE_IMAGE
//相冊(cè)刪除按鈕默認(rèn)不顯示 有內(nèi)容顯示
var shouldShowDeleteInAlbum = MutableLiveData(false)
private var deleteAlbumList = arrayListOf<Album>()
//添加需要?jiǎng)h除的相冊(cè)
fun addAlbumToDeleteList(album: Album){
deleteAlbumList.add(album)
shouldShowDeleteInAlbum.postValue(true)
}
fun deleteAlbumFromDeleteList(album: Album){
deleteAlbumList.remove(album)
shouldShowDeleteInAlbum.postValue(deleteAlbumList.size > 0)
}
//獲取相冊(cè)
fun loadAlbumsWithType(albumType:Int){
viewModelScope.launch(Dispatchers.IO) {
val result = repository.loadAlbumWithType(albumType)
result.collectLatest {
if (albumType == ALBUM_TYPE_IMAGE){
imageAlbumList.postValue(it)
}else{
videoAlbumList.postValue(it)
}
}
}
}
//插入相冊(cè)
fun addAlbum(name:String,type: Int){
viewModelScope.launch(Dispatchers.IO) {
val album = Album(
0,
name,
getApplication<Application>().DEFAULT_COVER_URL,
0,
type = type
)
repository.addAlbum(album)
}
}
}
五蕉朵、注意事項(xiàng)
- Room 在創(chuàng)建數(shù)據(jù)庫(kù)時(shí),不需要手動(dòng)編寫(xiě) SQL 語(yǔ)句創(chuàng)建表阳掐,它會(huì)根據(jù)關(guān)聯(lián)的數(shù)據(jù)實(shí)體類(lèi)創(chuàng)建對(duì)應(yīng)的表始衅。在定義數(shù)據(jù)庫(kù)時(shí),通過(guò) @Database 注解的 entities 參數(shù)指定數(shù)據(jù)庫(kù)中關(guān)聯(lián)的數(shù)據(jù)實(shí)體類(lèi)(即所包含的數(shù)據(jù)表)缭保。
- 數(shù)據(jù)庫(kù)關(guān)聯(lián)的數(shù)據(jù)實(shí)體類(lèi)必須是 @Entity 注解標(biāo)注的汛闸;
- 數(shù)據(jù)庫(kù)新增數(shù)據(jù)表時(shí),需要新建數(shù)據(jù)實(shí)體類(lèi)艺骂,并將實(shí)體類(lèi)添加到數(shù)據(jù)庫(kù)定義的關(guān)聯(lián)實(shí)體類(lèi)列表中诸老;
- 數(shù)據(jù)庫(kù)只在首次創(chuàng)建數(shù)據(jù)庫(kù)時(shí),根據(jù)定義的關(guān)聯(lián)數(shù)據(jù)庫(kù)實(shí)體類(lèi)創(chuàng)建表钳恕,無(wú)需編寫(xiě) SQL 語(yǔ)句别伏;
- 應(yīng)用在新版本添加表(或更改表結(jié)構(gòu)),需要進(jìn)行數(shù)據(jù)庫(kù)遷移(也就是常說(shuō)的數(shù)據(jù)庫(kù)升級(jí))忧额,否則數(shù)據(jù)庫(kù)不會(huì)創(chuàng)建新表(或更改表結(jié)構(gòu))畸肆。