架构图如上所示:Dao层操作数据库,dataBase 实例 需要基础 RoomDataBase 执行一下 数据库的一下操作,viewModel 层 提供dataBase
使用viewmodel层提供的database 去修改操作视图,Adapter 加载结合:AsyncListDiffer
开发步骤:
1.按照Google developer开发文档将Room 接入到项目中:https://developer.android.com/training/data-storage/room
主要组件
Room 包含三个主要组件:
- 数据库类,用于保存数据库并作为应用持久性数据底层连接的主要访问点。
- 数据实体,用于表示应用的数据库中的表。
- 数据访问对象 (DAO),提供您的应用可用于查询、更新、插入和删除数据库中的数据的方法。
数据库类为应用提供与该数据库关联的 DAO 的实例。反过来,应用可以使用 DAO 从数据库中检索数据,作为关联的数据实体对象的实例。此外,应用还可以使用定义的数据实体更新相应表中的行,或者创建新行供插入。
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
implementation("androidx.room:room-runtime:2.4.3")
annotationProcessor("androidx.room:room-compiler:2.4.3")
kapt("androidx.room:room-compiler:2.4.3")
2.定义数据实体
@Entity
data class User(
@PrimaryKey
var uid: Int,
@ColumnInfo(name = "first_name")
var firstName: String?,
@ColumnInfo(name = "last_name")
var lastName: String?
) {
override fun equals(any: Any?): Boolean {
if (any == null) {
return false
}
val other: User = any as User
return ((uid == other.uid && (firstName == other.firstName) && (lastName == other.lastName)))
}
}
3.数据访问对象Dao:
@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getAll(): LiveData<MutableList<User>>?
@Query("SELECT * FROM user")
fun getAll2(): MutableList<User>?
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
fun loadAllByIds(userIds: IntArray): List<User>
@Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1")
fun findByName(first: String, last: String): User
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOneUser(users: User)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertUserList(list: List<User>)
@Delete
fun delete(user: User)
@Query("SELECT * FROM user ORDER BY last_name ASC")
fun usersByLastName(): LiveData<List<User>>?
}
注意数据访问对象Dao在插入的时候注意添加下:onConflict = OnConflictStrategy.REPLACE 意思是插入或者更新:insert or update
4.定义数据库:
AppDatabase
定义数据库配置,并作为应用对持久性数据的主要访问点。数据库类必须满足以下条件:
- 该类必须带有 @Database 注解,该注解包含列出所有与数据库关联的数据实体的 entities 数组。
- 该类必须是一个抽象类,用于扩展 RoomDatabase。
- 对于与数据库关联的每个 DAO 类,数据库类必须定义一个具有零参数的抽象方法,并返回 DAO 类的实例。
@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getInstance(): AppDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(MyApplication.instance(), AppDatabase::class.java, "my-database-name")
.allowMainThreadQueries()
.build()
INSTANCE = instance
}
return instance
}
}
}
}
这里定义了一个AppDatabase单例,可以在全局中时候 AppDatabase实例
5.定义:ViewModel
class MyViewModel : ViewModel() {
fun getUsersList(): LiveData<MutableList<User>>? {
return AppDatabase.getInstance().userDao().getAll()
}
}
6.Activity中添加 viewModel的:observe:
viewModel.getUsersList()?.observe(this) {
//获取到db 的数据后,开始显示到View上
}
7.创建Adapter ,这里的Adapter 结合了:AsyncListDiffer:https://developer.android.com/reference/androidx/recyclerview/widget/AsyncListDiffer
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserViewHolder> {
private final AsyncListDiffer<User> mDiffer = new AsyncListDiffer(this, DIFF_CALLBACK);
@Override
public int getItemCount() {
int size = mDiffer.getCurrentList().size();
return size;
}
public void submitList(List<User> list) {
mDiffer.submitList(list);
}
@NonNull
@Override
public UserViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
return new UserViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_user_adapter, viewGroup, false));
}
@Override
public void onBindViewHolder(UserViewHolder holder, int position) {
User user = mDiffer.getCurrentList().get(position);
holder.bindView(user, position);
}
public class UserViewHolder extends RecyclerView.ViewHolder {
TextView mIdView;
TextView mFirstNameView;
TextView mLastNameView;
public UserViewHolder(@NonNull View itemView) {
super(itemView);
mIdView = itemView.findViewById(R.id.tv_id);
mFirstNameView = itemView.findViewById(R.id.tv_first_name);
mLastNameView = itemView.findViewById(R.id.tv_last_name);
}
public void bindView(User user, int position) {
mIdView.setText("Id:" + user.getUid());
mFirstNameView.setText(user.getFirstName());
mLastNameView.setText(user.getLastName());
}
}
public static final DiffUtil.ItemCallback<User> DIFF_CALLBACK = new DiffUtil.ItemCallback<User>() {
@Override
public boolean areItemsTheSame(
@NonNull User oldUser, @NonNull User newUser) {
// User properties may have changed if reloaded from the DB, but ID is fixed
return oldUser.getUid() == newUser.getUid();
}
@Override
public boolean areContentsTheSame(@NonNull User oldUser, @NonNull User newUser) {
// NOTE: if you use equals, your object must properly override Object#equals()
// Incorrectly returning false here will result in too many animations.
return oldUser.equals(newUser);
}
};
}
8.接下来就要实现数据的增删改查,同步显示在View上了:
插入数据:insert
mBinding.btnAddView.setOnClickListener {
mCoroutineScope.launch {
val localUserList = AppDatabase.getInstance().userDao().getAll2()
val count = localUserList?.size ?: 0
val targetCount = count + 1
val user = User(targetCount, "FirstName:" + targetCount, "LastName:" + targetCount)
AppDatabase.getInstance().userDao().insertOneUser(user)
}
}
更新数据:update
mBinding.btnEditView.setOnClickListener {
mCoroutineScope.launch {
val localUserList = AppDatabase.getInstance().userDao().getAll2()
if (localUserList?.isNullOrEmpty() == true) {
return@launch
}
localUserList?.forEach {
it.firstName = it.firstName + "A"
it.lastName = it.lastName + "B"
}
localUserList?.let {
AppDatabase.getInstance().userDao().insertUserList(it)
}
}
}
删除数据:delete:
mBinding.btnDeleteView.setOnClickListener {
mCoroutineScope.launch {
val localUserList = AppDatabase.getInstance().userDao().getAll2()
localUserList?.let {
val user = it?.get(it.size - 1)
if (user != null) {
AppDatabase.getInstance().userDao().delete(user)
}
}
}
}
到目前为止就已经实现了:数据库的增删改查,同步显示到View 上:这样Room,ViewModel, AsyncListDiffer 结合在了一起
Demo地址:GitHub - JasonZhangHG/DiffUtilDemo: Room DB + ViewModel + AsyncListDiffer