Android使用Kotlin协程简单封装Room工具类

2,045 阅读6分钟

Android使用Kotlin协程简单封装Room工具类

RoomUtils.png

1.前言:

之前讲解过room的简单使用和升级,项目中也使用过kotlin来写room数据库,和现在项目xutils中数据库的使用相比简直不要太爽,前面两篇博客也讲解了xutils使用过程中遇到的问题和解决方案,但是发现真的是太古老了,问题太多,这次重构就想着把之前的彻底推到重来,刚好项目决定采用Kotlin开发,于是尝试了一下在room中使用协程,发现使用起来太舒服了,代码简洁,逻辑清楚,封装成RoomUtils工具类后可以直接调用,直接上代码。

2.导入Room依赖

def room_version = "2.5.0"
def coroutine_version = "1.5.1"
​
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
implementation "androidx.room:room-rxjava2:$room_version"

image.png

3.创建实体类(Entity)

/**
 * @author: njb
 * @date: 2023/7/15 21:46
 * @desc:
 */
@Entity(tableName = "User")
data class User(
    var userId: String = "",
    @ColumnInfo(name = "name") var name: String = "",
    @ColumnInfo(name = "sex") var sex: String = "",
    @ColumnInfo(name = "email") var email: String = "",
){
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

image.png

4.创建数据访问对象(DAO)接口:

package com.example.roomdemo.dao
​
import androidx.room.*
import com.example.roomdemo.bean.User
​
/**
 * @author: njb
 * @date: 2023/7/15 21:36
 * @desc:
 */
@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    suspend fun getAll(): List<User>?
​
    @Query("SELECT * FROM user WHERE id IN (:userIds)")
    suspend fun loadAllByIds(userIds: IntArray): List<User>?
​
    @Query("SELECT * FROM user WHERE name LIKE :name")
    suspend fun findByName(name: String): User?
​
    @Query("SELECT *FROM user WHERE id LIKE:userId")
    suspend fun findById(userId: Int): User?
​
    @Update
    suspend fun updateUser(vararg user: User)
​
    @Insert
    suspend fun insertAll(vararg users: User)
​
    @Delete
    suspend fun delete(user: User)
}

5.创建操作管理类UserRepository:

package com.example.roomdemo.db
​
import androidx.room.Database
import androidx.room.RoomDatabase
import com.example.roomdemo.bean.User
import com.example.roomdemo.dao.UserDao
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
​
/**
 * @author: njb
 * @date: 2023/7/15 21:36
 * @desc:
 */
class UserRepository(private val userDao: UserDao) {
    suspend fun insertUser(user: User) {
        withContext(Dispatchers.IO) {
            userDao.insertAll(user)
        }
    }
​
    suspend fun updateUser(user: User) {
        withContext(Dispatchers.IO) {
            userDao.updateUser(user)
        }
    }
​
    suspend fun deleteUser(user: User) {
        withContext(Dispatchers.IO) {
            userDao.delete(user)
        }
    }
​
    suspend fun getAllUsers(): List<User> {
        return withContext(Dispatchers.IO) {
            userDao.getAll()!!
        }
    }
}

6.封装数据库工具类RoomUtils:

package com.example.roomdemo.db
​
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.example.roomdemo.app.MyApp
import com.example.roomdemo.bean.User
import com.example.roomdemo.dao.UserDao
​
/**
 * @author: njb
 * @date: 2023/7/15 21:47
 * @desc:
 */@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}
    class RoomUtils private constructor() {
​
        private val database: AppDatabase by lazy {
            Room.databaseBuilder(
                MyApp.instance.applicationContext,
                AppDatabase::class.java, "app-database"
            ).build()
        }
​
        companion object {
            @Volatile
            private var instance: RoomUtils? = null
​
            fun getInstance(): RoomUtils {
                return instance ?: synchronized(this) {
                    instance ?: RoomUtils().also { instance = it }
                }
            }
        }
​
        private val userRepository: UserRepository by lazy {
            UserRepository(database.userDao())
        }
​
        suspend fun insertUser(user: User) {
            userRepository.insertUser(user)
        }
​
        suspend fun updateUser(user: User) {
            userRepository.updateUser(user)
        }
​
        suspend fun deleteUser(user: User) {
            userRepository.deleteUser(user)
        }
​
        suspend fun getAllUsers(): List<User> {
            return userRepository.getAllUsers()
        }
}

7.简单使用:

​
​
class MainActivity : AppCompatActivity() {
    private lateinit var roomUtils: RoomUtils
​
​
    @OptIn(DelicateCoroutinesApi::class)
    private fun initDataBase() {
​
        roomUtils= RoomUtils.getInstance();
​
​
        GlobalScope.launch(Dispatchers.Main) {
​
            // 插入一条用户记录
            val user = User(12,"张三","女","zhangsan@example.com")
​
            withContext(Dispatchers.IO) {
                roomUtils.insertUser(user)
                Log.d("获取用户数据",user.name.toString())
            }
​
            // 更新一条用户记录
            val user1 = User(12,"欧四","女","zhangsan@example.com")
            withContext(Dispatchers.IO) {
                roomUtils.updateUser(user1)
            }
​
            // 删除一条用户记录
            withContext(Dispatchers.IO) {
                roomUtils.deleteUser(user)
            }
​
            // 获取所有用户记录
            val users = withContext(Dispatchers.IO) {
                roomUtils.getAllUsers()
            }
​
        }
    }
}

8.增删改查分开操作:

package com.example.roomdemo
​
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.blankj.utilcode.util.LogUtils
import com.example.roomdemo.bean.User
import com.example.roomdemo.db.RoomUtils
import kotlinx.coroutines.*
​
class MainActivity : AppCompatActivity() {
    private lateinit var roomUtils: RoomUtils
    private val tvAdd:TextView by lazy { findViewById(R.id.tv_add) }
    private val tvUpdate:TextView by lazy { findViewById(R.id.tv_update) }
    private val tvDelete:TextView by lazy { findViewById(R.id.tv_delete) }
    private val tvQuery:TextView by lazy { findViewById(R.id.tv_query) }
    private val tvDeleteUsers:TextView by lazy { findViewById(R.id.tv_delete_users) }
    private val tvDeleteAll:TextView by lazy { findViewById(R.id.tv_delete_all) }
    private val TAG: String = "MainLogUtils"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initDataBase()
        initView()
    }
​
    private fun initDataBase() {
        roomUtils = RoomUtils.getInstance();
    }
​
    @OptIn(DelicateCoroutinesApi::class)
    private fun initView() {
        tvAdd.setOnClickListener {
            GlobalScope.launch(Dispatchers.Main) {
                // 插入一条用户记录
                try {
                    val user = User("14","张三","女","zhangsan@example.com")
                    withContext(Dispatchers.IO) {
                        roomUtils.insertUser(user)
                        LogUtils.d(TAG, "添加用户数据$user")
                    }
                    tvAdd.text = user.toString()
                }catch (e:java.lang.Exception){
                    e.stackTrace
                    LogUtils.e(TAG,e.message.toString())
                }
            }
        }
​
        tvUpdate.setOnClickListener {
            GlobalScope.launch(Dispatchers.Main) {
                // 更新一条用户记录
                val user1 = User("12","欧四","男","ousi@example.com")
                withContext(Dispatchers.IO) {
                    roomUtils.updateUser(user1)
                }
                val user = roomUtils.getAllUsers()
                LogUtils.d(TAG, "===更新用户数据===$user")
                tvUpdate.text = user.toString()
            }
        }
​
        tvQuery.setOnClickListener {
            GlobalScope.launch(Dispatchers.Main) {
                // 获取所有用户记录
               val user =  withContext(Dispatchers.IO) {
                     roomUtils.getAllUsers()
                }
                LogUtils.d(TAG, "===获取所有用户数据===$user")
                tvQuery.text = user.toString()
            }
        }
​
        tvDelete.setOnClickListener {
            GlobalScope.launch(Dispatchers.Main) {
                val user = User("12","张三","女","zhangsan@example.com")
                // 删除一条用户记录
                withContext(Dispatchers.IO) {
                    roomUtils.deleteUser(user)
                    LogUtils.d(TAG, "===删除用户数据===$user")
                }
                val user1 = roomUtils.getAllUsers()
                tvDelete.text = user1.toString()
            }
        }
​
        tvDeleteUsers.setOnClickListener {
            GlobalScope.launch(Dispatchers.Main) {
                val user = roomUtils.getAllUsers()
                val user1 : List<User>
                // 删除所有用户
                withContext(Dispatchers.IO) {
                    roomUtils.deleteUsers(user)
                    user1 = roomUtils.getAllUsers()
                    LogUtils.d(TAG, "===删除多个用户数据===$user1")
                }
                tvDeleteUsers.text = user1.toString()
            }
        }
​
        tvDeleteAll.setOnClickListener {
            GlobalScope.launch(Dispatchers.Main) {
                val user : List<User>
                // 删除所有用户
                withContext(Dispatchers.IO) {
                    roomUtils.deleteAllUser()
                    user = roomUtils.getAllUsers()
                    LogUtils.d(TAG, "===删除所有用户数据===$user")
                }
                tvDeleteAll.text = user.toString()
            }
        }
    }
}

9.布局代码:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">
​
    <TextView
        android:id="@+id/tv_add"
        android:layout_width="180dp"
        android:layout_height="60dp"
        android:text="添加数据"
        android:textColor="@color/white"
        android:textSize="18sp"
        android:gravity="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="30dp"
        android:background="@color/design_default_color_primary"/>
​
    <TextView
        android:id="@+id/tv_update"
        android:layout_width="180dp"
        android:layout_height="60dp"
        android:text="修改数据"
        android:textColor="@color/white"
        android:textSize="18sp"
        android:gravity="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_add"
        android:layout_marginTop="30dp"
        android:background="@color/design_default_color_primary"/>
​
    <TextView
        android:id="@+id/tv_query"
        android:layout_width="180dp"
        android:layout_height="60dp"
        android:text="查询数据"
        android:textColor="@color/white"
        android:textSize="18sp"
        android:gravity="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_update"
        android:layout_marginTop="30dp"
        android:background="@color/design_default_color_primary"/>
    <TextView
        android:id="@+id/tv_delete"
        android:layout_width="180dp"
        android:layout_height="60dp"
        android:text="删除据"
        android:textColor="@color/white"
        android:textSize="18sp"
        android:gravity="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_query"
        android:layout_marginTop="30dp"
        android:background="@color/design_default_color_primary"/>
​
</androidx.constraintlayout.widget.ConstraintLayout>

10.遇到问题:

10.1 运行时报错提示版本有问题:

解决方法:把Room版本升级到最新的2.5.0,当然2.4.0也是没问题的,由于我的插件版本是7.4.2,项目的目标版本是33,

所以我为了统一,把room版本也升级成最新的了.

image.png

10.2 生成数据库时一直报错:

刚开始以为是方法添加协程导致的,去掉后发现没有用,查找各种资料最后回看以前的项目才发现问题所在,真是好久没用新的组件,基本的配置都忘记了,真是粗心大意啊~~重要的事情说三遍:

一定要记得配置room的文件生成路径!!

一定要记得配置room的文件生成路径!!

一定要记得配置room的文件生成路径!!

//指定room.schemaLocation生成的文件路径
javaCompileOptions {
    annotationProcessorOptions {
        arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
    }
}

image.png

10.3 添加数据时不可添加重复的id:

这里是指id作为主键和key,实际项目中数据都是从后台获取的,id不会重复,先看错误的写法:

/**
 * @author: njb
 * @date: 2023/7/15 21:46
 * @desc:
 */
@Entity(tableName = "User")
data class User(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "name") var name: String?,
    @ColumnInfo(name = "sex") var sex: String?,
    @ColumnInfo(name = "email") var email: String?,
)

正确的写法:id作为主键,而且是自增的,正常项目很少遇到此问题,这里只是测试demo.

/**
 * @author: njb
 * @date: 2023/7/15 21:46
 * @desc:
 */
@Entity(tableName = "User")
data class User(
    var userId: String = "",
    @ColumnInfo(name = "name") var name: String = "",
    @ColumnInfo(name = "sex") var sex: String = "",
    @ColumnInfo(name = "email") var email: String = "",
){
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

image.png

11.增删改查日志打印:

image.png

image.png

image.png

image.png

image.png

image.png

12.实现的效果如下:

image.png

image.png

image.png

13.文章总结:

以上就是今天的使用协程操作数据库简单使用和封装,遇到了几个简单的问题,但是最后找到原因都解决了,而且发现使用协程后封装了roomUilts非常简单,操作起来也方便,增删改查各种操作都极其简单,大家如果感兴趣的可以去尝试下,大胆干快点散,要勇于尝试新技术,新的东西写起来很有意思,让人瞬间头脑清醒,路漫漫其修远兮,吾将上下而求索!!

14.项目源码:

gitee.com/jackning_ad…