一、Room
1、Room 概述
- Navigation 是 Jetpack 中的一个重要成员,它是一个持久化库,它为管理数据库提供了简单强大的方法
2、Room 引入
- 在模块级
build.gradle
中引入相关依赖
implementation "androidx.room:room-runtime:2.2.5"
annotationProcessor "androidx.room:room-compiler:2.2.5"
二、Room 简单案例
1、Application
- MyApplication.java
package com.my.room.application;
import android.app.Application;
import android.content.Context;
public class MyApplication extends Application {
public static final String TAG = MyApplication.class.getSimpleName();
private static Context context;
@Override
public void onCreate() {
super.onCreate();
context = this;
}
public static Context getContext() {
return context;
}
}
2、Entity
- User.java
package com.my.room.entity;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "User")
public class User {
@PrimaryKey(autoGenerate = true)
public Long id;
@ColumnInfo(name = "name")
public String name;
@ColumnInfo(name = "age")
public Integer age;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
3、DAO
- UserDAO.java
package com.my.room.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import com.my.room.entity.User;
import java.util.List;
@Dao
public interface UserDAO {
@Insert
void insert(User user);
@Delete
void delete(User user);
@Update
void update(User user);
@Query("SELECT * FROM User")
List<User> queryAll();
}
4、Database
package com.my.room.database;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import com.my.room.application.MyApplication;
import com.my.room.dao.UserDAO;
import com.my.room.entity.User;
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {
public static final String TAG = MyDatabase.class.getSimpleName();
public static final String DATABASE_NAME = "test.db";
private static MyDatabase myDatabase;
public static MyDatabase getInstance() {
if (myDatabase == null) {
myDatabase = Room.databaseBuilder(MyApplication.getContext(), MyDatabase.class, DATABASE_NAME)
.build();
}
return myDatabase;
}
public abstract UserDAO getUserDAO();
}
5、Repository Observer
- InsertObserver.java
package com.my.room.repository.observer;
public interface InsertObserver {
void onSuccess();
void onError(String msg);
}
- DeleteObserver.java
package com.my.room.repository.observer;
public interface DeleteObserver {
void onSuccess();
void onError(String msg);
}
- UpdateObserver.java
package com.my.room.repository.observer;
public interface UpdateObserver {
void onSuccess();
void onError(String msg);
}
- QueryAllObserver.java
package com.my.room.repository.observer;
import java.util.List;
public interface QueryAllObserver<T> {
void onResult(List<T> ts);
}
6、Repository
- UserRepository.java
package com.my.room.repository;
import android.os.AsyncTask;
import android.os.Handler;
import com.my.room.dao.UserDAO;
import com.my.room.database.MyDatabase;
import com.my.room.entity.User;
import com.my.room.repository.observer.DeleteObserver;
import com.my.room.repository.observer.InsertObserver;
import com.my.room.repository.observer.QueryAllObserver;
import com.my.room.repository.observer.UpdateObserver;
import java.util.List;
public class UserRepository {
private MyDatabase myDatabase;
private UserDAO userDAO;
private Handler handler;
public UserRepository() {
myDatabase = MyDatabase.getInstance();
userDAO = myDatabase.getUserDAO();
handler = new Handler();
}
// ====================================================================================================
public void insert(User user, InsertObserver insertObserver) {
new InsertAsyncTask(user, insertObserver).execute();
}
public void delete(User user, DeleteObserver deleteObserver) {
new DeleteAsyncTask(user, deleteObserver).execute();
}
public void update(User user, UpdateObserver updateObserver) {
new UpdateAsyncTask(user, updateObserver).execute();
}
public void queryAll(QueryAllObserver<User> queryAllObserver) {
new QueryAllAsyncTask(queryAllObserver).execute();
}
// ====================================================================================================
private class InsertAsyncTask extends AsyncTask<Void, Void, Void> {
private User user;
private InsertObserver insertObserver;
public InsertAsyncTask(User user, InsertObserver insertObserver) {
this.user = user;
this.insertObserver = insertObserver;
}
@Override
protected Void doInBackground(Void... voids) {
try {
userDAO.insert(user);
if (insertObserver != null) handler.post(() -> insertObserver.onSuccess());
} catch (Exception e) {
e.printStackTrace();
if (insertObserver != null)
handler.post(() -> insertObserver.onError(e.getMessage()));
}
return null;
}
}
private class DeleteAsyncTask extends AsyncTask<Void, Void, Void> {
private User user;
private DeleteObserver deleteObserver;
public DeleteAsyncTask(User user, DeleteObserver deleteObserver) {
this.user = user;
this.deleteObserver = deleteObserver;
}
@Override
protected Void doInBackground(Void... voids) {
try {
userDAO.delete(user);
if (deleteObserver != null) handler.post(() -> deleteObserver.onSuccess());
} catch (Exception e) {
e.printStackTrace();
if (deleteObserver != null)
handler.post(() -> deleteObserver.onError(e.getMessage()));
}
return null;
}
}
private class UpdateAsyncTask extends AsyncTask<Void, Void, Void> {
private User user;
private UpdateObserver updateObserver;
public UpdateAsyncTask(User user, UpdateObserver updateObserver) {
this.user = user;
this.updateObserver = updateObserver;
}
@Override
protected Void doInBackground(Void... voids) {
try {
userDAO.update(user);
if (updateObserver != null) handler.post(() -> updateObserver.onSuccess());
} catch (Exception e) {
e.printStackTrace();
if (updateObserver != null)
handler.post(() -> updateObserver.onError(e.getMessage()));
}
return null;
}
}
private class QueryAllAsyncTask extends AsyncTask<Void, Void, Void> {
private QueryAllObserver<User> queryAllObserver;
public QueryAllAsyncTask(QueryAllObserver<User> queryAllObserver) {
this.queryAllObserver = queryAllObserver;
}
@Override
protected Void doInBackground(Void... voids) {
try {
List<User> users = userDAO.queryAll();
handler.post(() -> queryAllObserver.onResult(users));
} catch (Exception e) {
handler.post(() -> queryAllObserver.onResult(null));
}
return null;
}
}
}
7、Activity Layout
- activity_user.java
<?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=".UserActivity"
tools:ignore="MissingConstraints">
<Button
android:id="@+id/btn_insert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="增"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="删"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_insert" />
<Button
android:id="@+id/btn_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="改"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_delete" />
<Button
android:id="@+id/btn_query_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="查"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_update" />
</androidx.constraintlayout.widget.ConstraintLayout>
8、Activity Code
- UserActivity.java
package com.my.room;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import com.my.room.entity.User;
import com.my.room.repository.UserRepository;
import com.my.room.repository.observer.DeleteObserver;
import com.my.room.repository.observer.InsertObserver;
import com.my.room.repository.observer.UpdateObserver;
public class UserActivity extends AppCompatActivity {
public static final String TAG = UserActivity.class.getSimpleName();
private UserRepository userRepository;
private Button btnInsert;
private Button btnDelete;
private Button btnUpdate;
private Button btnQueryAll;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
userRepository = new UserRepository();
btnInsert = findViewById(R.id.btn_insert);
btnDelete = findViewById(R.id.btn_delete);
btnUpdate = findViewById(R.id.btn_update);
btnQueryAll = findViewById(R.id.btn_query_all);
btnInsert.setOnClickListener(v -> {
User user = new User();
user.name = "jack";
user.age = 20;
userRepository.insert(user, new InsertObserver() {
@Override
public void onSuccess() {
Log.i(TAG, "------------------------------ insert success");
}
@Override
public void onError(String msg) {
Log.i(TAG, "------------------------------ insert error - " + msg);
}
});
});
btnDelete.setOnClickListener(v -> {
User user = new User();
user.id = 1L;
userRepository.delete(user, new DeleteObserver() {
@Override
public void onSuccess() {
Log.i(TAG, "------------------------------ delete success");
}
@Override
public void onError(String msg) {
Log.i(TAG, "------------------------------ delete error - " + msg);
}
});
});
btnUpdate.setOnClickListener(v -> {
User user = new User();
user.id = 1L;
user.name = "jack";
user.age = 30;
userRepository.update(user, new UpdateObserver() {
@Override
public void onSuccess() {
Log.i(TAG, "------------------------------ update success");
}
@Override
public void onError(String msg) {
Log.i(TAG, "------------------------------ update error - " + msg);
}
});
});
btnQueryAll.setOnClickListener(v -> {
userRepository.queryAll(users -> {
if (users == null) {
Log.i(TAG, "------------------------------ queryAll - users is null");
return;
}
if (users.size() == 0) {
Log.i(TAG, "------------------------------ queryAll - users is empty");
return;
}
for (User user : users)
Log.i(TAG, "------------------------------ queryAll - " + user);
});
});
}
}
Test
- 增 -> 改 -> 查 -> 删 -> 查,输出结果
I/UserActivity: ------------------------------ insert success
I/UserActivity: ------------------------------ update success
I/UserActivity: ------------------------------ queryAll - User{id=1, name='jack', age=30}
I/UserActivity: ------------------------------ delete success
I/UserActivity: ------------------------------ queryAll - users is empty
三、Room 简单案例解析
1、Entity 解析
@Entity(tableName = "User")
public class User {
@PrimaryKey(autoGenerate = true)
public Long id;
@ColumnInfo(name = "name")
public String name;
@ColumnInfo(name = "age")
public Integer age;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
@Entity(tableName = "User")
:指定 User 类为 Room 数据库中 User 表对应的实体类
-
@PrimaryKey(autoGenerate = true)
:指定 id 字段为主键,并且它的值是自动生成的 -
@ColumnInfo(name = "name")
:指定 name 字段映射到表中的 name 列,@ColumnInfo(name = "age")
同理
- User 表会在 APP 首次安装时由 Room 根据 User 类创建,SQL 语句如下
CREATE TABLE IF NOT EXISTS User (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
age INTEGER
);
2、DAO 解析
@Dao
public interface UserDAO {
@Insert
void insert(User user);
@Delete
void delete(User user);
@Update
void update(User user);
@Query("SELECT * FROM User")
List<User> queryAll();
}
@Dao
:指定 UserDAO 接口与 Room 数据库进行交互,以操作 User 类,该接口使用 Room 相关注解来定义操作方法
-
@Insert
:插入数据,这里传递一个 User 对象作为参数,Room 将这个对象插入到数据库中 -
@Delete
:删除数据,这里传递一个 User 对象作为参数,Room 基于该对象的 id 字段来删除相应的行 -
@Update
:修改数据,这里传递一个 User 对象作为参数,Room 基于该对象的 id 字段来修改相应的行 -
@Query("SELECT * FROM User")
:查询数据,查询表中的所有行
3、Database 解析
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {
public static final String TAG = MyDatabase.class.getSimpleName();
public static final String DATABASE_NAME = "test.db";
private static MyDatabase myDatabase;
public static MyDatabase getInstance() {
if (myDatabase == null) {
myDatabase = Room.databaseBuilder(MyApplication.getContext(), MyDatabase.class, DATABASE_NAME)
.build();
}
return myDatabase;
}
public abstract UserDAO getUserDAO();
}
(1)注解解析
@Database
:指定 MyDatabase 抽象类为 Room 数据库
-
entities = {User.class}
:指定这个数据库包含 User 类 -
version = 1
:指定数据库的版本号,当实体或数据库结构发生变化时,需要增加这个版本号 -
exportSchema = false
:指定不导出数据库的 schema
(2)思路解析
-
单例模式确保整个应用程序中只有一个 MyDatabase 实例对象
-
通过
Room.databaseBuilder
方法构建数据库,该方法需要三个参数,分别为上下文、数据库类、数据库名称 -
定义获取 UserDao 实例对象的抽象方法,Room 将自动生成这个方法的实现
4、Repository 解析
public class UserRepository {
private MyDatabase myDatabase;
private UserDAO userDAO;
private Handler handler;
public UserRepository() {
myDatabase = MyDatabase.getInstance();
userDAO = myDatabase.getUserDAO();
handler = new Handler();
}
// ====================================================================================================
public void insert(User user, InsertObserver insertObserver) {
new InsertAsyncTask(user, insertObserver).execute();
}
public void delete(User user, DeleteObserver deleteObserver) {
new DeleteAsyncTask(user, deleteObserver).execute();
}
public void update(User user, UpdateObserver updateObserver) {
new UpdateAsyncTask(user, updateObserver).execute();
}
public void queryAll(QueryAllObserver<User> queryAllObserver) {
new QueryAllAsyncTask(queryAllObserver).execute();
}
// ====================================================================================================
private class InsertAsyncTask extends AsyncTask<Void, Void, Void> {
private User user;
private InsertObserver insertObserver;
public InsertAsyncTask(User user, InsertObserver insertObserver) {
this.user = user;
this.insertObserver = insertObserver;
}
@Override
protected Void doInBackground(Void... voids) {
try {
userDAO.insert(user);
if (insertObserver != null) handler.post(() -> insertObserver.onSuccess());
} catch (Exception e) {
e.printStackTrace();
if (insertObserver != null)
handler.post(() -> insertObserver.onError(e.getMessage()));
}
return null;
}
}
private class DeleteAsyncTask extends AsyncTask<Void, Void, Void> {
private User user;
private DeleteObserver deleteObserver;
public DeleteAsyncTask(User user, DeleteObserver deleteObserver) {
this.user = user;
this.deleteObserver = deleteObserver;
}
@Override
protected Void doInBackground(Void... voids) {
try {
userDAO.delete(user);
if (deleteObserver != null) handler.post(() -> deleteObserver.onSuccess());
} catch (Exception e) {
e.printStackTrace();
if (deleteObserver != null)
handler.post(() -> deleteObserver.onError(e.getMessage()));
}
return null;
}
}
private class UpdateAsyncTask extends AsyncTask<Void, Void, Void> {
private User user;
private UpdateObserver updateObserver;
public UpdateAsyncTask(User user, UpdateObserver updateObserver) {
this.user = user;
this.updateObserver = updateObserver;
}
@Override
protected Void doInBackground(Void... voids) {
try {
userDAO.update(user);
if (updateObserver != null) handler.post(() -> updateObserver.onSuccess());
} catch (Exception e) {
e.printStackTrace();
if (updateObserver != null)
handler.post(() -> updateObserver.onError(e.getMessage()));
}
return null;
}
}
private class QueryAllAsyncTask extends AsyncTask<Void, Void, Void> {
private QueryAllObserver<User> queryAllObserver;
public QueryAllAsyncTask(QueryAllObserver<User> queryAllObserver) {
this.queryAllObserver = queryAllObserver;
}
@Override
protected Void doInBackground(Void... voids) {
try {
List<User> users = userDAO.queryAll();
handler.post(() -> queryAllObserver.onResult(users));
} catch (Exception e) {
handler.post(() -> queryAllObserver.onResult(null));
}
return null;
}
}
}
- UserRepository 类是对 userDAO 的封装,封装的主要目的是异步任务处理
(1)异步任务处理
- 对于每种 UserDAO 实例对象的操作都定义了一个继承 AsyncTask 的内部类,它用于安排操作在后台线程中执行,然后使用观察者模式来通知调用者
-
InsertAsyncTask:负责在后台线程中执行插入操作,完成后(无论成功还是失败)通过 InsertObserver 通知调用者
-
DeleteAsyncTask:负责在后台线程中执行删除操作,完成后(无论成功还是失败)通过 DeleteObserver 通知调用者
-
UpdateAsyncTask:负责在后台线程中执行插入操作,完成后(无论成功还是失败)通过 UpdateObserver 通知调用者
-
QueryAllAsyncTask:负责在后台线程中执行查询操作,完成后(无论成功还是失败)通过 QueryAllObserver 通知调用者
(2)补充
-
线程调度:数据库操作不能在 UI 线程执行,操作在后台线程中执行完成后,使用 Handler 来回到 UI 线程执行后续操作
-
异常处理:在异步任务种,捕获可能抛出的异常,例如,执行插入操作时主键冲突会抛出异常