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

@Entity
表結(jié)構(gòu)實(shí)體@PrimaryKey
主鍵@ColumnInfo
列/字段信息
二、用Room+ViewModel+LiveData增刪改查數(shù)據(jù)
用 Entity
、Dao
、Database
操作數(shù)據(jù)庫(kù)
數(shù)據(jù)庫(kù)的每個(gè)表,都對(duì)應(yīng)一個(gè)Entity
,一個(gè)Dao
(Dao
負(fù)責(zé)增刪改查操作)
項(xiàng)目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)建一個(gè)數(shù)據(jù)表Student
。先創(chuàng)建包名,在com.bignerdranch.roomdemo
下創(chuàng)建一個(gè)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會(huì)使用這個(gè)構(gòu)造器來(lái)存儲(chǔ)數(shù)據(jù),也就是當(dāng)你從表中得到Student對(duì)象時(shí)候,Room會(huì)使用這個(gè)構(gòu)造器*/constructor(id: Int, name: String, age: String) {this.id = idthis.name = namethis.age = age}/*** 由于Room只能識(shí)別和使用一個(gè)構(gòu)造器,如果希望定義多個(gè)構(gòu)造器,你可以使用Ignore標(biāo)簽,讓Room忽略這個(gè)構(gòu)造器* 同樣,@Ignore標(biāo)簽還可用于字段,使用@Ignore標(biāo)簽標(biāo)記過(guò)的字段,Room不會(huì)持久化該字段的數(shù)據(jù)*/@Ignoreconstructor(name: String, age: String) {this.name = namethis.age = age}
}
這個(gè)@Entity
就是表示數(shù)據(jù)庫(kù)中的表,Student
類對(duì)應(yīng)就是Student
表,@PrimaryKey
表示主鍵,這里是id
,autoGenerate = true
是自增,@NonNull
表示不為空。 @ColumnInfo
表示表中的列名,name = "name"
表示列名的值。
下面在db
包下新建一個(gè)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
是一個(gè)接口,主要是定義了一些方法,通過(guò)注解在編譯的時(shí)候會(huì)生成實(shí)現(xiàn)類。
然后,新建main/assets/databases
,并在其中放置student.db
文件,其位置如下:

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


然后,在MyDatabase.kt
中,用createFromAsset()
方法從assets/database/students.db
創(chuàng)建Room
數(shù)據(jù)庫(kù),下面是數(shù)據(jù)庫(kù)的創(chuàng)建,在db
包下新建一個(gè)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
注解表示這個(gè)類是用來(lái)操作數(shù)據(jù)庫(kù)的,entities = [Student::class]
表示當(dāng)前數(shù)據(jù)庫(kù)中的表,只有一個(gè)Student
表,多的表用應(yīng)用逗號(hào)隔開。version = 1
表示數(shù)據(jù)庫(kù)的版本,可以做數(shù)據(jù)庫(kù)的升級(jí)操作。createFromAsset()
是Room庫(kù)中提供的。
注意這是一個(gè)抽象類,在編譯時(shí)Room
會(huì)幫助構(gòu)建實(shí)現(xiàn)類。
現(xiàn)在運(yùn)行一下,手機(jī)或者模擬器都可以。然后什么都不用去做。

可以查看到,MyDatabase
和StudentDao
的實(shí)現(xiàn)類都自動(dòng)生成了。
ViewModel
內(nèi):通過(guò) Room.Database
查到LiveData
數(shù)據(jù),在外部監(jiān)聽LiveData
當(dāng) Room
變化時(shí),通過(guò)ViewModel
內(nèi)的LiveData
通知頁(yè)面數(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()}}
表操作無(wú)非就是那么幾個(gè),增刪改查,但是為了更直觀的顯示結(jié)果,需要對(duì)UI做一些改動(dòng)。
在工程的build.gradle
中增加repositories
閉包中增加jitpack
庫(kù)。

然后在app
的build.gradle
中的dependencies{}
比包中增加
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.6'
1. 修改布局
Sync
一下,下面修改一下頁(yè)面的布局文件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>
這里就是一個(gè)按鈕和一個(gè)列表,下面創(chuàng)建列表的item
布局
在layout
下新建一個(gè)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
下新建一個(gè)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
,實(shí)例化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}
}
運(yùn)行后,當(dāng)LiveData
數(shù)據(jù)變化時(shí),更新UI
即可,而不需要每次增刪改后都必須用QueryStudentTask()
來(lái)手動(dòng)查詢一次數(shù)據(jù)庫(kù),簡(jiǎn)化了系統(tǒng),效果如下:

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