本文地址:https://blog.csdn.net/qq_40785165/article/details/114682348,转载需附上此地址
大家好,我是小黑,一个还没秃头的程序员~~~
命运如同手中的掌纹,无论多曲折,终掌握在自己手中。
这段时间有接触到页面缓存,就想到了Jetpack的Room组件,比起Sqlite更简洁方便,引用官方的话介绍Room
Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。
处理大量结构化数据的应用可极大地受益于在本地保留这些数据。最常见的用例是缓存相关数据。这样,当设备无法访问网络时,用户仍可在离线状态下浏览相应内容。设备重新连接到网络后,用户发起的所有内容更改都会同步到服务器
文章关键字:【Room】【LiveData】【ViewModel】【Repository】【AsyncTask】
说明
- LiveData:观察数据的变化并更新UI
- Reposity:数据获取逻辑的决策层,决定数据获取的策略
- ViewModel:Activity与Reposity的媒介,可以保存数据不丢失(如避免旋转屏幕时数据丢失)
- AsyncTask:轻量级异步处理,因为Room不允许在主线程进行操作,所以需要另开线程进行处理并返回主线程进行更新UI
效果如下,共四个操作------清空、增加、删除、更新,源码地址:https://gitee.com/fjjxxy/room-java-demo.git
话不多说,正文开始
(一)添加依赖
dependencies {
def room_version = "2.2.6"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// optional - RxJava support for Room
implementation "androidx.room:room-rxjava2:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"
// optional - Test helpers
testImplementation "androidx.room:room-testing:$room_version"
}
(二)配置编译器选项
注意事项:如果使用的是jdk8,请将room.incremental配置成false,否则会报错,报错的建议解决方案为改用jdk11,我还没试过这个方法
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments += [
"room.schemaLocation":"$projectDir/schemas".toString(),
"room.incremental":"false",
"room.expandProjection":"true"]
}
}
}
(三)Room组件一:UserBean(Entity),定义若干属性的实体类,用来表示数据库中的表,定义列名以及列的属性,代码如下:
//默认情况下,Room 将类名称用作数据库表名称。如果您希望表具有不同的名称,请设置 @Entity 注释的 tableName 属性,否则dao的操作语句会报红
//indices = arrayOf() 添加索引字段
//@Fts4(languageId = "lid") 指定存储语言
//@Fts4 通过全文搜索 (FTS) 快速访问数据库信息
@Entity(
tableName = "user"
)
public class UserBean {
//kotlin下自增属性无效,原因未知
@PrimaryKey(autoGenerate = true)
private Integer uid;
//默认情况下,Room 会为实体中定义的每个字段创建一个列。如果某个实体中有您不想保留的字段,则可以使用 @Ignore 为这些字段添加注释
//如果有类继承了这个类,那就直接用@Entity(ignoredColumns = arrayOf("xxx"))忽略一些字段
@Ignore
private Bitmap picture;
//Room 将字段名称用作数据库中的列名称。如果您希望列具有不同的名称,请将 @ColumnInfo 注释添加到字段
@ColumnInfo(
name = "first_name"
)
private String firstName = "你好";
@ColumnInfo(
name = "last_name"
)
private String lastName = "李四";
public Integer getUid() {
return this.uid;
}
public void setUid(Integer var1) {
this.uid = var1;
}
public Bitmap getPicture() {
return this.picture;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String var1) {
this.firstName = var1;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String var1) {
this.lastName = var1;
}
说明:
- @Entity:指定表名
- @PrimaryKey(autoGenerate = true):定义为主键并自增
- @Ignore:不在数据库表中的字段
(四)Room组件二:UserDao(Dao)包含用于访问数据的方法以及数据库语句,这里的查询返回的是LiveData,方便数据变化时及时更新UI,代码如下:
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
LiveData<List<UserBean>> getAll();
@Query("SELECT * FROM user where uid=:userId")
LiveData<List<UserBean>> getUserById(int userId);
@Query("SELECT * FROM user where first_name like :firstName")
List<UserBean> getUserByCondition(String firstName);
@Insert
Long[] insertUser(UserBean... var1);
@Delete
int deleteUser(UserBean... var1);
@Query("delete from user")
int deleteAll();
@Update
int updateUser(UserBean... var1);
}
说明:
- @Query: 是 DAO 类中使用的主要注释。它允许您对数据库执行读/写操作,返回类型要正确,不然会报错
- @Insert:插入数据,可以返回插入数据的id/id数组
- @Delete:根据主键删除给定的实体
- @Update:根据主键查询并更新实体属性
(五)Room组件三:DatabaseHelper(RoomDatabase),是RoomDatabase的拓展抽象类,用来返回Dao,代码如下:
注意事项:这里的Database是因为有AsyncTask的原因所以可以直接访问数据库,也可通过在.build()之前设置allowMainThreadQueries()在主线程进行数据库访问
@Database(
entities = {UserBean.class},
version = 1
)
public abstract class DatabaseHelper extends RoomDatabase {
public abstract UserDao getUserDao();
private static DatabaseHelper mDatabaseHelper;
//单例,user是数据库名
public static synchronized DatabaseHelper getDataBase(Context context) {
if (mDatabaseHelper == null) {
mDatabaseHelper = Room.databaseBuilder(context.getApplicationContext(), DatabaseHelper.class, "user")
.build();
}
return mDatabaseHelper;
}
}
(六)使用Reposity进行解耦,在Reposity层进行AsyncTask异步访问数据库,并提供调用函数,UserRepository代码如下:
public class UserRepository {
private LiveData<List<UserBean>> mListLiveData;
private final UserDao mUserDao;
private Context mContext;
public UserRepository(Context context) {
mContext = context;
DatabaseHelper databaseHelper = DatabaseHelper.getDataBase(context.getApplicationContext());
mUserDao = databaseHelper.getUserDao();
mListLiveData = mUserDao.getAll();
}
public LiveData<List<UserBean>> getListLiveData() {
return mListLiveData;
}
public void insert(UserBean... userBeans) {
new InsertAsyncTask(mUserDao, mContext).execute(userBeans);
}
public void delete(UserBean... userBeans) {
new DeleteAsyncTask(mUserDao, mContext).execute(userBeans);
}
public void deleteAll() {
new DeleteAllAsyncTask(mUserDao, mContext).execute();
}
public void update(UserBean... userBeans) {
new UpdateAsyncTask(mUserDao, mContext).execute(userBeans);
}
private static class InsertAsyncTask extends AsyncTask<UserBean, Void, Long[]> {
private UserDao mUserDao;
private Context mContext;
public InsertAsyncTask(UserDao userDao, Context context) {
mUserDao = userDao;
mContext = context;
}
@Override
protected Long[] doInBackground(UserBean... userBeans) {
return this.mUserDao.insertUser(userBeans);
}
@Override
protected void onPostExecute(Long[] longs) {
if (longs.length > 0) {
Toast.makeText(mContext, "新增成功", Toast.LENGTH_SHORT).show();
}
}
}
private static class DeleteAsyncTask extends AsyncTask<UserBean, Void, Integer> {
private UserDao mUserDao;
private Context mContext;
public DeleteAsyncTask(UserDao userDao, Context context) {
mUserDao = userDao;
mContext = context;
}
@Override
protected Integer doInBackground(UserBean... userBeans) {
return this.mUserDao.deleteUser(userBeans);
}
@Override
protected void onPostExecute(Integer integer) {
if (integer > 0) {
Toast.makeText(mContext, "删除成功", Toast.LENGTH_SHORT).show();
}
}
}
private static class DeleteAllAsyncTask extends AsyncTask<Void, Void, Integer> {
private UserDao mUserDao;
private Context mContext;
public DeleteAllAsyncTask(UserDao userDao, Context context) {
mUserDao = userDao;
mContext = context;
}
@Override
protected Integer doInBackground(Void... voids) {
return this.mUserDao.deleteAll();
}
@Override
protected void onPostExecute(Integer integer) {
if (integer > 0) {
Toast.makeText(mContext, "删除成功", Toast.LENGTH_SHORT).show();
}
}
}
private static class UpdateAsyncTask extends AsyncTask<UserBean, Void, Integer> {
private UserDao mUserDao;
private Context mContext;
public UpdateAsyncTask(UserDao userDao, Context context) {
mUserDao = userDao;
mContext = context;
}
@Override
protected Integer doInBackground(UserBean... userBeans) {
return this.mUserDao.updateUser(userBeans);
}
@Override
protected void onPostExecute(Integer integer) {
if (integer > 0) {
Toast.makeText(mContext, "更新成功", Toast.LENGTH_SHORT).show();
}
}
}
}
(七)在ViewModel中定义访问Repository业务的函数,供Activity调用,继承AndroidViewModel 可以传递上下文,UserViewModel代码如下:
public class UserViewModel extends AndroidViewModel {
private final UserRepository mUserRepository;
public UserViewModel(@NonNull Application application) {
super(application);
mUserRepository = new UserRepository(application);
}
public LiveData<List<UserBean>> getListLiveData() {
return mUserRepository.getListLiveData();
}
public void insert(UserBean... userBeans) {
mUserRepository.insert(userBeans);
}
public void delete(UserBean... userBeans) {
mUserRepository.delete(userBeans);
}
public void deleteAll() {
mUserRepository.deleteAll();
}
public void update(UserBean... userBeans) {
mUserRepository.update(userBeans);
}
(八)在Activity中使用ViewModel层,并在LiveData数据有变化时更新UI,MainActivity代码如下:
public class MainActivity extends AppCompatActivity {
private TextView mTvDeleteAllUser;
private TextView mTvAdd;
private TextView mTvDelete;
private TextView mTvUpdate;
private TextView mTvResult;
private List<UserBean> mList = new ArrayList<>();
private UserViewModel mUserViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvDeleteAllUser = findViewById(R.id.tv_delete_all_user);
mTvAdd = findViewById(R.id.tv_add);
mTvDelete = findViewById(R.id.tv_delete);
mTvUpdate = findViewById(R.id.tv_update);
mTvResult = findViewById(R.id.tv_result);
mUserViewModel = new ViewModelProvider(this, new SavedStateViewModelFactory(getApplication(), this)).get(UserViewModel.class);
//观察数据的变化
mUserViewModel.getListLiveData().observe(this, userBeans -> {
mList.clear();
mList.addAll(userBeans);
mTvResult.setText(getUserResultString(userBeans));
});
mTvDeleteAllUser.setOnClickListener(v -> mUserViewModel.deleteAll());
mTvAdd.setOnClickListener(v -> {
mUserViewModel.insert(new UserBean());
});
mTvDelete.setOnClickListener(v -> {
if (mList.size() > 0) {
mUserViewModel.delete(mList.get(mList.size() - 1));
}
});
mTvUpdate.setOnClickListener(v -> {
if (mList.size() <= 0) return;
UserBean userBean = mList.get(mList.size() - 1);
userBean.setLastName("改动的数据");
mUserViewModel.update(userBean);
});
}
private String getUserResultString(List<UserBean> list) {
StringBuilder stringBuilder = new StringBuilder();
for (UserBean userBean : list) {
if (stringBuilder.length() > 0) {
stringBuilder.append(",");
}
stringBuilder.append(userBean.getUid() + "(" + userBean.getLastName() + ")");
}
return stringBuilder.toString();
}
}
至此,关于Room数据库访问的内容就实现了,使用起来会比sqlite简单便捷许多,用来做内容缓存是个不错的选择,最后,也希望喜欢我文章的朋友们可以帮忙点赞、收藏、评论,也可以关注一下,如果有问题可以在评论区提出,谢谢大家的支持与阅读!