如何維護(hù)網(wǎng)站百度seo排名優(yōu)化軟件化
一、Room組件概述
Room是Android JetPack架構(gòu)組件之一,是一個(gè)持久處理的庫。Room提供了在SQLite數(shù)據(jù)庫上提供抽象層,使之實(shí)現(xiàn)數(shù)據(jù)訪問。
(1)實(shí)體類(Entity):映射并封裝了數(shù)據(jù)庫對(duì)應(yīng)的數(shù)據(jù)表中對(duì)應(yīng)的結(jié)構(gòu)化數(shù)據(jù)。實(shí)體定義了數(shù)據(jù)庫中的數(shù)據(jù)表。實(shí)體類中的數(shù)據(jù)域與表的列一一對(duì)應(yīng)。
(2)數(shù)據(jù)訪問對(duì)象(Data Access Object,DAO):在DAO中定義了訪問數(shù)據(jù)庫的常見的操作(例如插入、刪除、修改和檢索等),以達(dá)到實(shí)現(xiàn)創(chuàng)建映射數(shù)據(jù)表的實(shí)體類對(duì)象,以及對(duì)該實(shí)體類對(duì)象實(shí)例的屬性值進(jìn)行設(shè)置和獲取的目的。
(3)數(shù)據(jù)庫(Room Database):表示對(duì)數(shù)據(jù)庫基本信息的描述,包括數(shù)據(jù)庫的版本、名稱、包含的實(shí)體類和提供的DAO對(duì)象實(shí)例。Room組件中的所有的數(shù)據(jù)庫必須擴(kuò)展為RoomDatabase抽象類,從而實(shí)現(xiàn)對(duì)實(shí)際SQLite數(shù)據(jù)庫的封裝。
二、Room組件的配置
在移動(dòng)應(yīng)用所在的模塊對(duì)應(yīng)的build.gradle中需要進(jìn)行如下配置:
(1) 增加插件
Groovy DSL:
plugins {……id 'kotlin-kapt'
}
Kotlin DSL:
plugins{
...id("kotlin-kapt")
}
kotlin-kapt實(shí)現(xiàn)標(biāo)注(注解)處理
(2)增加標(biāo)注處理的配置
Groovy DSL定義:
android {……defaultConfig {……//增加標(biāo)注處理,增加Schema保存的路徑javaCompileOptions{annotationProcessorOptions{//定義標(biāo)注處理器選項(xiàng)arguments +=["room.schemaLocation":"$projectDir/schemas".toString(),"room.incremental":"true","room.expandProjection":"true"]}}
}
Kotlin DSL定義:
android {……defaultConfig {……//增加標(biāo)注處理javaCompileOptions{annotationProcessorOptions{//定義標(biāo)注處理器選項(xiàng)arguments +=mapOf("room.schemaLocation" to "$projectDir/schemas".toString(),"room.incremental" to "true","room.expandProjection" to "true")}}
}
(3)增加相關(guān)依賴
Groovy DSL定義
def room_version = "2.5.0"implementation"androidx.room:room-runtime:$room_version"// 注釋處理工具annotationProcessor "androidx.room:room-compiler:$room_version"// Kotlin注釋處理工具(kapt)kapt"androidx.room:room-compiler:$room_version"// kotlin擴(kuò)展和協(xié)同程序?qū)oom的支持implementation "androidx.room:room-ktx:$room_version"
如果在處理數(shù)據(jù)庫是需要采用rxJava3來實(shí)現(xiàn)異步處理,這時(shí)還需要增加RxJava3庫
//增加RxJava庫的依賴implementation "io.reactivex.rxjava3:rxjava:3.0.7"//增加在Android對(duì)RxJava庫的支持implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'// RxJava3implementation "androidx.room:room-rxjava3:$room_version"
Kotlin DSL定義依賴:
val room_version = "2.5.0"implementation("androidx.room:room-runtime:$room_version")annotationProcessor("androidx.room:room-compiler:$room_version")kapt("androidx.room:room-compiler:$room_version")// kotlin擴(kuò)展和協(xié)同程序?qū)oom的支持implementation("androidx.room:room-ktx:$room_version")// RxJava2implementation("androidx.room:room-rxjava2:$room_version")// RxJava3implementation("androidx.room:room-rxjava3:$room_version")//增加RxJava庫的依賴implementation("io.reactivex.rxjava3:rxjava:3.0.7")//增加在Android對(duì)RxJava庫的支持implementation("io.reactivex.rxjava3:rxandroid:3.0.0")
如果在構(gòu)建模塊的過程中,出現(xiàn)了java版本不兼容的情況,可以調(diào)整:
android{
...compileOptions {sourceCompatibility = JavaVersion.VERSION_17targetCompatibility = JavaVersion.VERSION_17}kotlinOptions {jvmTarget = "17"}
}
三、Room組件實(shí)現(xiàn)數(shù)據(jù)庫的處理
新建一個(gè)項(xiàng)目,實(shí)現(xiàn)對(duì)多位學(xué)生的信息寫入數(shù)據(jù)庫并執(zhí)行檢索和CRUD操作。
3.1 創(chuàng)建實(shí)體類
映射并封裝了數(shù)據(jù)庫對(duì)應(yīng)的數(shù)據(jù)表中對(duì)應(yīng)的結(jié)構(gòu)化數(shù)據(jù)。實(shí)體定義了數(shù)據(jù)庫中的數(shù)據(jù)表。實(shí)體類中的數(shù)據(jù)域與表的列一一對(duì)應(yīng)。
@Entity(tableName = "students")
data class Student(@PrimaryKey(autoGenerate = true)@ColumnInfo(name= "studentId") val id:Long,@ColumnInfo(name= "studentNo") val no:String?,@ColumnInfo(name= "studentName") val name:String,@ColumnInfo(name= "studentScore") val score:Int,@ColumnInfo(name = "studentGrade") val grade:String?)
{@Ignoreconstructor(no:String,name:String,score:Int,grade:String):this(0,no,name,score,grade)
}
定義的實(shí)體類Student與數(shù)據(jù)表students對(duì)應(yīng)。通過標(biāo)注@Entity(tableName = “students”)來指定實(shí)體類對(duì)應(yīng)的數(shù)據(jù)表。并對(duì)實(shí)體類的屬性定義通過標(biāo)注@ColumnInfo,對(duì)應(yīng)于數(shù)據(jù)表students中的各個(gè)字段,并通過@PrimaryKey標(biāo)注來指定數(shù)據(jù)表的關(guān)鍵字。
注意:Room只能識(shí)別和使用一個(gè)構(gòu)造器,如果存在多個(gè)構(gòu)造器可以使用@Ignore讓Room忽略這個(gè)構(gòu)造器。因此在上述代碼中constructor定義的輔助構(gòu)造器增加了標(biāo)注@Ignore。
3.2 創(chuàng)建數(shù)據(jù)訪問對(duì)象DAO
在數(shù)據(jù)訪問對(duì)象DAO是一個(gè)接口,定義了對(duì)指定數(shù)據(jù)表希望能執(zhí)行的CRUD操作。
@Dao
interface StudentDAO {/*** 插入記錄*/@Insertfun insertStudent(student:Student):Long/*** 刪除記錄*/@Updatefun updateStudent(student:Student)/*** 刪除記錄*/@Deletefun deleteStudent(student:Student)/*** 檢索所有的記錄*/@Query("select * from students")fun queryAllStudents():List<Student>/*** 檢索指定學(xué)號(hào)的學(xué)生記錄*/@Query("select * from students where studentNo = :no")fun queryStudentByNo(no:String):Student}
3.3 創(chuàng)建數(shù)據(jù)庫
必須定義一個(gè)RoomDatabase的抽象子類來表示對(duì)數(shù)據(jù)庫基本信息的描述,包括數(shù)據(jù)庫的版本、名稱、包含的實(shí)體類和提供的DAO對(duì)象實(shí)例。通過數(shù)據(jù)庫類來達(dá)到對(duì)實(shí)際SQLite數(shù)據(jù)庫的封裝。
@Database(entities = [Student::class], version = 1)
abstract class StudentDatabase : RoomDatabase() {abstract fun studentDao(): StudentDAOcompanion object{private var instance: StudentDatabase? = null/*** 單例模式創(chuàng)建為一個(gè)StudentDatabase對(duì)象實(shí)例*/@Synchronizedfun getInstance(context: Context): StudentDatabase {instance?.let{return it}return Room.databaseBuilder(context,StudentDatabase::class.java,"studentDB.db").build()}}
}
@Database標(biāo)注表示抽象的類對(duì)應(yīng)數(shù)據(jù)庫,內(nèi)部包括的數(shù)據(jù)表由標(biāo)注內(nèi)部的屬性entities指定。如果數(shù)據(jù)庫包括多個(gè)數(shù)據(jù)表,entitites可以指定多個(gè)實(shí)體類的類對(duì)象。
在上述的代碼中,采用了單例模式,使得在整個(gè)移動(dòng)應(yīng)用中只有一個(gè)數(shù)據(jù)庫的對(duì)象實(shí)例,在獲取這個(gè)唯一實(shí)例時(shí),只有一個(gè)線程可以訪問,因此在getInstance方法中設(shè)置標(biāo)注@Synchronized。在這個(gè)方法指定創(chuàng)建的數(shù)據(jù)庫名是studentDB.db
3.4 定義并配置應(yīng)用類
因?yàn)樵趹?yīng)用中需要獲取上下文,因此定義應(yīng)用類,并在AndroidManifest進(jìn)行配置,使之易于獲取applicationContext上下文對(duì)象。
3.4.1定義應(yīng)用類
class MainApp: Application() {@SuppressLint("StaticFieldLeaked")companion object{lateinit var context: Context}override fun onCreate() {super.onCreate()context = applicationContext}
}
3.4.2 在AndroidManifest.xml配置應(yīng)用類
在AndroidManifest.xml中需要在application元素中指定已定義的應(yīng)用類MainApp,類似如下代碼
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:name=".MainApp" ... >
</application>
</manifest>
3.5 測(cè)試數(shù)據(jù)庫的訪問
在MainActivity中定義對(duì)數(shù)據(jù)庫的測(cè)試代碼。
class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {testDB()}}/*** 測(cè)試數(shù)據(jù)庫*/fun testDB() {Observable.create<Student> { emitter ->//獲得Dao對(duì)象val dao = StudentDatabase.getInstance(MainApp.context).studentDao()//插入記錄,測(cè)試數(shù)據(jù)庫版本,將下列注釋取消dao.insertStudent(Student("6001013", "李四", 87, "良好"))//檢索記錄val students = dao.queryAllStudents()for (student in students)emitter.onNext(student)}.subscribeOn(Schedulers.io())//指定被觀察者的線程處理I/O 操作.observeOn(AndroidSchedulers.mainThread())//指定觀察者的線程為主線程.subscribe {Log.d("Ch10_05", "${it}")}}
}
四、Room組件實(shí)現(xiàn)數(shù)據(jù)庫的遷移
移動(dòng)應(yīng)用的需求的變化,也會(huì)導(dǎo)致數(shù)據(jù)庫不斷地升級(jí)。在數(shù)據(jù)庫升級(jí)時(shí),會(huì)希望保留原有的數(shù)據(jù)。因此,Room提供了數(shù)據(jù)庫遷移的方式來解決數(shù)據(jù)庫的升級(jí)。
Room庫提供了Migration 類實(shí)現(xiàn)數(shù)據(jù)庫增量遷移。每個(gè) Migration 子類提供了Migration.migrate() 函數(shù)實(shí)現(xiàn)新舊版本數(shù)據(jù)庫之間的遷移路徑。當(dāng)移動(dòng)應(yīng)用需要升級(jí)數(shù)據(jù)庫時(shí),Room 庫會(huì)利用一個(gè)或多個(gè) Migration 子類運(yùn)行 migrate() 函數(shù),在運(yùn)行時(shí)將數(shù)據(jù)庫遷移到最新版本。
在上述的模塊的基礎(chǔ)上,要求修改數(shù)據(jù)庫中數(shù)據(jù)表students的結(jié)構(gòu),增加一個(gè)新的字studentAddress,這時(shí)需要修改上述代碼來完成具體的功能。
4.1 修改實(shí)體類
修改實(shí)體類Student,增加一個(gè)屬性address,并映射數(shù)據(jù)表students的字段studentAddress,代碼如下:
@Entity(tableName = "students")
data class Student(@PrimaryKey(autoGenerate = true)@ColumnInfo(name="studentId") val id:Long,@ColumnInfo(name="studentNo") val no:String?,@ColumnInfo(name="studentName") val name:String,@ColumnInfo(name="studentScore") val score:Int,@ColumnInfo(name = "studentGrade") val grade:String?,@ColumnInfo(name="studentAddress") val address:String?){@Ignoreconstructor(no:String,name:String,score:Int,grade:String,address:String):this(0,no,name,score,grade,address)
}
4.2 修改數(shù)據(jù)庫
因?yàn)閿?shù)據(jù)表變化,這時(shí)需要修改數(shù)據(jù)庫,變更數(shù)據(jù)庫的版本為2。定義Migration對(duì)象,指定數(shù)據(jù)庫遷移是從版本1遷移到版本2,并覆蓋migrate的方法,執(zhí)行具體遷移的操作。
@Database(entities = [Student::class], version = 2)
abstract class StudentDatabase : RoomDatabase() {abstract fun studentDao(): StudentDAOcompanion object{private var instance: StudentDatabase? = null//數(shù)據(jù)庫從版本1遷移到版本2val MIGRATION_1_2 = object : Migration(1, 2) {//遷移方法定義override fun migrate(database: SupportSQLiteDatabase) {//修改數(shù)據(jù)表students,增加一個(gè)新的字段address,數(shù)據(jù)類型為TEXT字符串database.execSQL("ALTER TABLE students ADD COLUMN studentAddress TEXT")}}/*** 單例模式創(chuàng)建為一個(gè)StudentDatabase對(duì)象實(shí)例*/@Synchronizedfun getInstance(context:Context):StudentDatabase{instance?.let{return it}return Room.databaseBuilder(context,StudentDatabase::class.java,"studentDB.db").addMigrations(MIGRATION_1_2).build().apply{instance = this}}}
}
在上述代碼的getInstance返回?cái)?shù)據(jù)庫對(duì)象時(shí),通過調(diào)用addMigrations進(jìn)行處理遷移的操作。
4.3 修改測(cè)試代碼
在上述修改的前提基礎(chǔ)上,因數(shù)據(jù)庫的變更,測(cè)試代碼也進(jìn)行修改,代碼如下:
class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {testDB()}}/*** 數(shù)據(jù)庫版本2的測(cè)試函數(shù)*/fun testDB(){Observable.create<Student>{ emitter ->//獲得Dao對(duì)象val dao = StudentDatabase.getInstance(MainApp.context).studentDao()//插入記錄dao.insertStudent(Student("6001015","王五",87,"良好","江西省南昌紅谷大道999號(hào)"))//檢索記錄val students = dao.queryAllStudents()for(student in students)emitter.onNext(student)}.subscribeOn(Schedulers.io())//指定被觀察者的線程處理I/O 操作.observeOn(AndroidSchedulers.mainThread())//指定觀察者的線程為主線程.subscribe{ it: Student ->Log.d("TAG","${it}")}}
}
參考文獻(xiàn)
陳軼《Android移動(dòng)應(yīng)用開發(fā)(微課版)》[M] 北京:清華大學(xué)出版社 2022 P407-P419