電子商務(wù)網(wǎng)站建設(shè)與實踐上機指導(dǎo)教程網(wǎng)絡(luò)營銷最火的案例
文章目錄
- 一、Room簡介
- 二、用Room+ViewModel+LiveData增刪改查數(shù)據(jù)
- 三、下載源碼
一、Room簡介
Room
是Google
推出的數(shù)據(jù)庫框架,是一個 ORM
(Object Relational Mapping
)對象關(guān)系映射數(shù)據(jù)庫、其底層還是對SQLite
的封裝。
Room
包含三個主要組件:
- 數(shù)據(jù)庫類(
DataBase
),用于保存數(shù)據(jù)庫并作為應(yīng)用持久性數(shù)據(jù)底層連接的主要訪問點。 - 數(shù)據(jù)實體(
Entity
),用于表示應(yīng)用的數(shù)據(jù)庫中的表。 - 數(shù)據(jù)訪問對象 (
DAO
),提供您的應(yīng)用可用于查詢、更新、插入和刪除數(shù)據(jù)庫中的數(shù)據(jù)的方法。

@Entity
表結(jié)構(gòu)實體@PrimaryKey
主鍵@ColumnInfo
列/字段信息
二、用Room+ViewModel+LiveData增刪改查數(shù)據(jù)
用 Entity
、Dao
、Database
操作數(shù)據(jù)庫
數(shù)據(jù)庫的每個表,都對應(yīng)一個Entity
,一個Dao
(Dao
負(fù)責(zé)增刪改查操作)
項目RoomDemo,如下圖:

在build.gradle
添加如下依賴:
plugins {id 'com.android.application'id 'kotlin-android'id 'kotlin-android-extensions'id 'kotlin-kapt'
}dependencies {def room_version = "2.3.0"implementation "androidx.room:room-runtime:$room_version"annotationProcessor "androidx.room:room-compiler:$room_version"kapt "androidx.room:room-compiler:$room_version"
}
首先,創(chuàng)建一個數(shù)據(jù)表Student
。先創(chuàng)建包名,在com.bignerdranch.roomdemo
下創(chuàng)建一個db
包,db
下創(chuàng)建bean
包,bean
包下創(chuàng)建Student
類。
package com.bignerdranch.roomdemo.db.beanimport androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import org.jetbrains.annotations.NotNull@Entity(tableName = "student")
class Student {@NotNull@PrimaryKey(autoGenerate = true)@ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)var id = 0@NotNull@ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)var name: String@NotNull@ColumnInfo(name = "age", typeAffinity = ColumnInfo.TEXT)var age: String/*** Room會使用這個構(gòu)造器來存儲數(shù)據(jù),也就是當(dāng)你從表中得到Student對象時候,Room會使用這個構(gòu)造器*/constructor(id: Int, name: String, age: String) {this.id = idthis.name = namethis.age = age}/*** 由于Room只能識別和使用一個構(gòu)造器,如果希望定義多個構(gòu)造器,你可以使用Ignore標(biāo)簽,讓Room忽略這個構(gòu)造器* 同樣,@Ignore標(biāo)簽還可用于字段,使用@Ignore標(biāo)簽標(biāo)記過的字段,Room不會持久化該字段的數(shù)據(jù)*/@Ignoreconstructor(name: String, age: String) {this.name = namethis.age = age}
}
這個@Entity
就是表示數(shù)據(jù)庫中的表,Student
類對應(yīng)就是Student
表,@PrimaryKey
表示主鍵,這里是id
,autoGenerate = true
是自增,@NonNull
表示不為空。 @ColumnInfo
表示表中的列名,name = "name"
表示列名的值。
下面在db
包下新建一個dao
包,創(chuàng)建StudentDao
,里面的代碼如下:
package com.bignerdranch.roomdemo.db.daoimport androidx.lifecycle.LiveData
import androidx.room.*
import com.bignerdranch.roomdemo.db.bean.Student@Dao
interface StudentDao {@Insertfun insertStudent(student: Student?)@Deletefun deleteStudent(student: Student?)@Updatefun updateStudent(student: Student?)@Query("SELECT * FROM student")fun getStudentList(): LiveData<List<Student?>?>? //希望監(jiān)聽學(xué)生表的變化,為其加上LiveData@Query("SELECT * FROM student WHERE id = :id")fun getStudentById(id: Int): Student?
}
StudentDao
是一個接口,主要是定義了一些方法,通過注解在編譯的時候會生成實現(xiàn)類。
然后,新建main/assets/databases
,并在其中放置student.db
文件,其位置如下:

用SQLite
軟件導(dǎo)入students.db
文件,如下圖:


然后,在MyDatabase.kt
中,用createFromAsset()
方法從assets/database/students.db
創(chuàng)建Room
數(shù)據(jù)庫,下面是數(shù)據(jù)庫的創(chuàng)建,在db
包下新建一個MyDatabase
類,繼承RoomDatabase
,代碼如下:
package com.bignerdranch.roomdemo.dbimport android.content.Context
import android.util.Log
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.bignerdranch.roomdemo.db.bean.Student
import com.bignerdranch.roomdemo.db.dao.StudentDao@Database(entities = [Student::class], exportSchema = false, version = 1)
abstract class MyDatabase() : RoomDatabase() {abstract fun studentDao(): StudentDao?companion object {private val DATABASE_NAME = "my_db"private var databaseInstance: MyDatabase? = null@Synchronized //已同步fun getInstance(context: Context): MyDatabase? {if (databaseInstance == null) {databaseInstance = Room.databaseBuilder(context.applicationContext,MyDatabase::class.java,DATABASE_NAME).createFromAsset("databases/student.db").fallbackToDestructiveMigration()
// .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_1_3, MIGRATION_3_4).build()}return databaseInstance}val MIGRATION_1_2: Migration = object : Migration(1, 2) {override fun migrate(database: SupportSQLiteDatabase) {//do somethingLog.d("MyDatabase", "MIGRATION_1_2")}}private val MIGRATION_2_3: Migration = object : Migration(2, 3) {override fun migrate(database: SupportSQLiteDatabase) {//do somethingLog.d("MyDatabase", "MIGRATION_2_3")}}private val MIGRATION_1_3: Migration = object : Migration(1, 3) {override fun migrate(database: SupportSQLiteDatabase) {//do somethingLog.d("MyDatabase", "MIGRATION_1_3")}}val MIGRATION_3_4: Migration = object : Migration(3, 4) {override fun migrate(database: SupportSQLiteDatabase) {Log.d("MyDatabase", "MIGRATION_3_4")database.execSQL("CREATE TABLE temp_Student (" +"id INTEGER PRIMARY KEY NOT NULL," +"name TEXT," +"age TEXT)")database.execSQL("INSERT INTO temp_Student (id, name, age) " +"SELECT id, name, age FROM Student")database.execSQL("DROP TABLE Student")database.execSQL("ALTER TABLE temp_Student RENAME TO Student")}}}
}
這里的@Database
注解表示這個類是用來操作數(shù)據(jù)庫的,entities = [Student::class]
表示當(dāng)前數(shù)據(jù)庫中的表,只有一個Student
表,多的表用應(yīng)用逗號隔開。version = 1
表示數(shù)據(jù)庫的版本,可以做數(shù)據(jù)庫的升級操作。createFromAsset()
是Room庫中提供的。
注意這是一個抽象類,在編譯時Room
會幫助構(gòu)建實現(xiàn)類。
現(xiàn)在運行一下,手機或者模擬器都可以。然后什么都不用去做。

可以查看到,MyDatabase
和StudentDao
的實現(xiàn)類都自動生成了。
ViewModel
內(nèi):通過 Room.Database
查到LiveData
數(shù)據(jù),在外部監(jiān)聽LiveData
當(dāng) Room
變化時,通過ViewModel
內(nèi)的LiveData
通知頁面數(shù)據(jù)的變化,架構(gòu)如下:

新建StudentViewModel
類,該類繼承自AndroidViewModel
,其中有Database
和LiveData
,代碼如下:
package com.bignerdranch.roomdemo.jetpackroomwithlivedataandviewmodeltestimport android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import com.bignerdranch.roomdemo.db.MyDatabase
import com.bignerdranch.roomdemo.db.bean.Studentclass StudentViewModel(application: Application) : AndroidViewModel(application) {private val myDatabase: MyDatabase?val liveDataStudent: LiveData<List<Student?>?>?init {myDatabase = MyDatabase.getInstance(application)liveDataStudent = myDatabase!!.studentDao()!!.getStudentList()}}
表操作無非就是那么幾個,增刪改查,但是為了更直觀的顯示結(jié)果,需要對UI做一些改動。
在工程的build.gradle
中增加repositories
閉包中增加jitpack
庫。

然后在app
的build.gradle
中的dependencies{}
比包中增加
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.6'
1. 修改布局
Sync
一下,下面修改一下頁面的布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Buttonandroid:id="@+id/btnInsertStudent"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:layout_margin="12dp"android:text="Add a Student"android:textAllCaps="false" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="wrap_content" />
</LinearLayout>
這里就是一個按鈕和一個列表,下面創(chuàng)建列表的item
布局
在layout
下新建一個list_item_student.xml
布局,代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingTop="12dp"android:paddingBottom="12dp"><TextViewandroid:id="@+id/tvId"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center" /><TextViewandroid:id="@+id/tvName"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center" /><TextViewandroid:id="@+id/tvAge"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center" /></LinearLayout>
新建dialog_layout_student.xml
,布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><EditTextandroid:id="@+id/etName"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:autofillHints=""android:hint="Name" /><EditTextandroid:id="@+id/etAge"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:autofillHints=""android:hint="Age" /></LinearLayout>
在com.bignerdranch.roomdemo
下新建一個adapter
包,包下新建StudentAdapter
類,作為列表數(shù)據(jù)的適配器。代碼如下:
2. 列表適配器
package com.bignerdranch.roomdemo.adapterimport com.bignerdranch.roomdemo.R
import com.bignerdranch.roomdemo.db.bean.Student
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import kotlinx.android.synthetic.main.list_item_student.view.*class StudentAdapter(layoutResId: Int = R.layout.list_item_student) :BaseQuickAdapter<Student, BaseViewHolder>(layoutResId) {override fun convert(holder: BaseViewHolder, item: Student) {holder.itemView.run {tvId.text = item.id.toString()tvName.text = item.nametvAge.text = item.age}}
}
然后,在MainActivity
中初始化List
,實例化StudentViewModel
并監(jiān)聽其LiveData
的變化,代碼如下:
package com.bignerdranch.roomdemoimport android.content.DialogInterface
import android.os.AsyncTask
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.bignerdranch.roomdemo.adapter.StudentAdapter
import com.bignerdranch.roomdemo.db.MyDatabase
import com.bignerdranch.roomdemo.db.bean.Student
import com.bignerdranch.roomdemo.jetpackroomwithlivedataandviewmodeltest.StudentViewModel
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.listener.OnItemLongClickListener
import kotlinx.android.synthetic.main.activity_main.recyclerView/**** @date*/
class MainActivity : AppCompatActivity(), OnItemLongClickListener {private val mStudentAdapter by lazy {StudentAdapter().apply {setOnItemLongClickListener(this@MainActivity)}}private var myDatabase: MyDatabase? = nullprivate var studentList: MutableList<Student>? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)findViewById<View>(R.id.btnInsertStudent).setOnClickListener {openAddStudentDialog()}studentList = ArrayList()val layoutManager = LinearLayoutManager(this@MainActivity)layoutManager.orientation = LinearLayoutManager.VERTICALrecyclerView.layoutManager = layoutManagerrecyclerView.adapter = mStudentAdapter
// mStudentAdapter.setList(studentList)myDatabase = MyDatabase.getInstance(this)val studentViewModel: StudentViewModel =ViewModelProvider(this)[StudentViewModel::class.java]studentViewModel.liveDataStudent!!.observe(this) { students ->(studentList as ArrayList<Student?>).clear()(studentList as ArrayList<Student?>).addAll(students!!)
// studentAdapter2!!.notifyDataSetChanged()mStudentAdapter.setList(studentList)mStudentAdapter.notifyDataSetChanged()}}private fun updateOrDeleteDialog(student: Student?) {val options = arrayOf("更新", "刪除")AlertDialog.Builder(this@MainActivity).setTitle("").setItems(options) { _, which ->if (which == 0) {openUpdateStudentDialog(student)} else if (which == 1) {if (student != null) {DeleteStudentTask(student).execute()}}}.show()}private fun openAddStudentDialog() {val customView: View = this.layoutInflater.inflate(R.layout.dialog_layout_student, null)val etName = customView.findViewById<EditText>(R.id.etName)val etAge = customView.findViewById<EditText>(R.id.etAge)val builder = AlertDialog.Builder(this@MainActivity)val dialog = builder.create()dialog.setTitle("Add Student")dialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK") { dialog, which ->if (TextUtils.isEmpty(etName.text.toString()) || TextUtils.isEmpty(etAge.text.toString())) {Toast.makeText(this@MainActivity, "輸入不能為空", Toast.LENGTH_SHORT).show()} else {InsertStudentTask(etName.text.toString(), etAge.text.toString()).execute()}}dialog.setButton(DialogInterface.BUTTON_NEGATIVE,"CANCEL") { dialog, which -> dialog.dismiss() }dialog.setView(customView)dialog.show()}private fun openUpdateStudentDialog(student: Student?) {if (student == null) {return}val customView: View = this.layoutInflater.inflate(R.layout.dialog_layout_student, null)val etName = customView.findViewById<EditText>(R.id.etName)val etAge = customView.findViewById<EditText>(R.id.etAge)etName.setText(student.name)etAge.setText(student.age)val builder = AlertDialog.Builder(this@MainActivity)val dialog = builder.create()dialog.setTitle("Update Student")dialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK") { dialog, which ->if (TextUtils.isEmpty(etName.text.toString()) || TextUtils.isEmpty(etAge.text.toString())) {Toast.makeText(this@MainActivity, "輸入不能為空", Toast.LENGTH_SHORT).show()} else {UpdateStudentTask(student.id,etName.text.toString(),etAge.text.toString()).execute()}}dialog.setButton(DialogInterface.BUTTON_NEGATIVE,"CANCEL") { dialog, which -> dialog.dismiss() }dialog.setView(customView)dialog.show()}private inner class InsertStudentTask(var name: String, var age: String) :AsyncTask<Void?, Void?, Void?>() {override fun doInBackground(vararg params: Void?): Void? {myDatabase!!.studentDao()!!.insertStudent(Student(name, age))return null}}private inner class UpdateStudentTask(var id: Int, var name: String, var age: String) :AsyncTask<Void?, Void?, Void?>() {override fun doInBackground(vararg params: Void?): Void? {myDatabase!!.studentDao()!!.updateStudent(Student(id, name, age))return null}}private inner class DeleteStudentTask(var student: Student) : AsyncTask<Void?, Void?, Void?>() {override fun doInBackground(vararg params: Void?): Void? {myDatabase!!.studentDao()!!.deleteStudent(student)return null}}override fun onItemLongClick(adapter: BaseQuickAdapter<*, *>,view: View,position: Int): Boolean {updateOrDeleteDialog((studentList as ArrayList<Student?>)[position])return true}
}
運行后,當(dāng)LiveData
數(shù)據(jù)變化時,更新UI
即可,而不需要每次增刪改后都必須用QueryStudentTask()
來手動查詢一次數(shù)據(jù)庫,簡化了系統(tǒng),效果如下:

三、下載源碼
下載源碼github地址:https://github.com/caobin10/RoomDemo