Android has officially released an ORM framework and added it to Jetpack, called Room. This Demo uses room+ViewModel+liveData+viewBinding

This article realizes the effect diagram

Overall structure of Room

It is mainly composed of Entity, Dao, and Database. Each part has clear responsibilities, as detailed below.

Entity

Defines the entity classes that encapsulate the actual data. Each entity class has a table in the database, and the columns in the table are automatically generated based on the fields in the entity class.

Dao

Dao is the meaning of data access object, which usually encapsulates the operations of the database. In the actual programming, the logical layer does not need to deal with the underlying database, but can directly interact with the Dao layer.

Database

Dao is the meaning of data access object, which usually encapsulates the operations of the database. In the actual programming, the logical layer does not need to deal with the underlying database, but can directly interact with the Dao layer.

The implementation code

Add the dependent

The dependency needs to be added in app/bulid.gradle

Plugins {id 'kotlin-kapt' // Kotlin-kapt is only available in kotlin //ViewBinding buildFeatures {ViewBinding true} Dependencies {/ / lifecycle implementation "androidx lifecycle: lifecycle - extensions: 2.2.0" / / delete implementation side 'com. Making. McXtzhang: SwipeDelMenuLayout: V1.3.0' / / BaseRecyclerViewAdapterHelper can simplify the adapter code implementation 'com. Making. CymChad: BaseRecyclerViewAdapterHelper: 3.0.4' / / room implementation 'androidx. Room: room - the runtime: 2.1.0' kapt 'androidx. Room: room - the compiler: 2.1.0'}Copy the code

The Book entity class

Here we declare the Book class as an Entity class using the @entity annotation on the class name, and set the TAB name, then add the id field to the class, @primarykey as the PrimaryKey, and autoGenerate =true to increment, as well as the name and price fields

@Entity(tableName = "tb_book")
 class Book(var name:String,var price:Int) {
    @PrimaryKey(autoGenerate = true)
    var id:Long=0
}
Copy the code

BookDao

This is one of the most critical methods in Room, where all access to the database is encapsulated: using annotations, writing SQL statements that add, delete, change, and query

@dao interface BookDao {/** * add */ @insert fun insertBook(book: Book):Long /** * change the name by id */ @query ("UPDATE tb_book SET name=:name WHERE id=:id") fun UpdateBookPrice (name:String,id:Long) / @query ("SELECT * FROM tb_book") fun LoadAllBook ():LiveData<List<Book>> /** * DELETE Book by ID */ @query ("DELETE FROM tb_book WHERE id=:id") fun deleteBookById(id:Long):Int }Copy the code

AppDatabase

This section is written in a very fixed way, and only three parts need to be defined: the version number of the database, which entity classes are included, and the access instances that provide the Dao layer.

@Database(version = 1,entities = [Book::class],exportSchema = false) abstract class AppDatabase : RoomDatabase(){ abstract fun bookDao(): BookDao companion object{ private var instance:AppDatabase? =null @Synchronized fun getDatabase(context: Context):AppDatabase{ instance? .let { return it } return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java,"app_db") .build().apply { instance=this } } } }Copy the code

Repository

Object Repository {val bookDao: bookDao =AppDatabase.getDatabase(myapp.context).bookdao () // Query all fun LoadAllBook ():LiveData<List<Book>>{return bookdao.loadallBook ()} fun updateBookPrice(name:String,id:Long){ Bookdao.updatebookprice (name,id)} Book):Long{return bookdao.insertbook (Book)} // Delete fun deleteBookById(id:Long):Int{return bookDao.deleteBookById(id) } }Copy the code

MainViewModel

Class MainViewModel :ViewModel() {// Query all book fun getAll():LiveData<List< book >>{return Repository. LoadAllBook ()}}Copy the code

MainActivity

Note here that database operations are time-consuming operations. By default, Room does not allow database operations in the main thread, so we put the operation of adding, deleting and modifying the database into the child thread.

class MainActivity : AppCompatActivity() { private lateinit var binding:ActivityMainBinding private val list=ArrayList<Book>() private lateinit var adapter: BookAdapter private val mainViewModel by lazy { ViewModelProvider(this).get(MainViewModel::class.java) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding= ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // Initialize adapter adapter=BookAdapter(r.layout.item_book,list) binding.rvbook.adapter =adapter Mainviewmodel.getall ().observe(this, viewModel+liveData) Observer { list.clear() Log.d(TAG, "onCreate: The ${it. Size} ") list. AddAll (it) adapter. The notifyDataSetChanged ()}) / / registered click event Adapter. AddChildClickViewIds (R.i db tnDelete, R.i db tnUpdate) / / item child controls click event adapter. SetOnItemChildClickListener (object :OnItemChildClickListener{ override fun onItemChildClick( adapter: BaseQuickAdapter<*, *>, view: View, position: {Int) if (the id = = R.i db tnDelete) {/ / delete button thread {Repository. DeleteBookById (the list [position]. Id)}} the else If (the id = = R.i db tnUpdate) {/ / modify button thread {Repository. UpdateBookPrice (" I modified!!" , list [position]. Id)}}}}) / / add the binding btnAdd. SetOnClickListener {val name = binding. EdtBook. Text. The toString (). The trim () Val price = binding. EdtPrice. Text. The toString () if (name = = "" | | price = =" ") {Toast. MakeText (this, "please fill out the complete", Toast.LENGTH_SHORT).show() }else{ val book=Book(name,price.toInt()) thread { val id=Repository.insertBook(book) Log.d(TAG, "onCreate: ${id}") } } } } companion object { private const val TAG = "MainActivity" } }Copy the code

activity_main.xml

<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <LinearLayout android:id="@+id/ll_edt" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <EditText android:id="@+id/edt_book" Android :layout_width="wrap_content" Android :layout_height="50dp" Android: Layout_weight ="1" Android :hint=" Input name "/> <View android:layout_width="1dp" android:layout_height="match_parent" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:background="#000000"/> <EditText android:id="@+id/edt_price" Android :layout_width="wrap_content" Android: Layout_height ="50dp" Android: Layout_weight ="1" Android :hint=" input price "/> <Button Android :id="@+id/btn_add" Android :layout_width="wrap_content" Android :layout_height="wrap_content" Android :text=" add" /> </LinearLayout> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_book" android:layout_width="match_parent"  android:layout_height="match_parent" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> </LinearLayout>Copy the code

BookAdapter

class BookAdapter(layoutId:Int,list:ArrayList<Book>):BaseQuickAdapter<Book,BaseViewHolder>(layoutId,list){
    override fun convert(holder: BaseViewHolder, item: Book) {
        holder.setText(R.id.tv_name,item.name.toString())
        holder.setText(R.id.tv_price,item.price.toString())
    }

}
Copy the code

item_book.xml

SwipeMenuLayout is used for the parent layout of item. This is implemented using a dependency library that supports sideslipping.

<? The XML version = "1.0" encoding = "utf-8"? > <com.mcxtzhang.swipemenulib.SwipeMenuLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="60dp" android:layout_marginBottom="5dp" > <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="59dp"> <TextView android:id="@+id/tv_name" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" Android :text=" alive "/> <TextView Android :id="@+id/tv_price" Android :layout_width="0dp" Android :layout_height="match_parent"  android:layout_weight="1" android:gravity="center" android:text="12"/> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#d0d0d0"/> </LinearLayout> <Button android:id="@+id/btnUpdate" android:layout_width="60dp" android:layout_height="match_parent" Android :background="# Button "Android :background="# Button" Android :text=" # Button "android:background="# Button" android:background="# Button "Android :text=" # Button android:id="@+id/btnDelete" android:layout_width="60dp" android:layout_height="match_parent" Android :background="#ff0000" Android :text=" delete "Android :textColor="@android:color/white"/> </com.mcxtzhang.swipemenulib.SwipeMenuLayout>Copy the code

MyApp

So here we define the global Context. Don’t forget to register MyApp in androidmanifest.xml

class MyApp :Application(){
    companion object{
        lateinit var context: Context
    }
    override fun onCreate() {
        super.onCreate()
        context=applicationContext
    }
}
Copy the code

Note: Part of the content is from the first line of code Android version 3 by Guo Shen.