使用Jetpack组件Room(一)入门实践,让数据库访问更加高效简洁

本文地址:https://blog.csdn.net/qq_40785165/article/details/114682348,转载需附上此地址

大家好,我是小黑,一个还没秃头的程序员~~~

命运如同手中的掌纹,无论多曲折,终掌握在自己手中。

这段时间有接触到页面缓存,就想到了Jetpack的Room组件,比起Sqlite更简洁方便,引用官方的话介绍Room

Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。

处理大量结构化数据的应用可极大地受益于在本地保留这些数据。最常见的用例是缓存相关数据。这样,当设备无法访问网络时,用户仍可在离线状态下浏览相应内容。设备重新连接到网络后,用户发起的所有内容更改都会同步到服务器

文章关键字:【Room】【LiveData】【ViewModel】【Repository】【AsyncTask】

说明

  1. LiveData:观察数据的变化并更新UI
  2. Reposity:数据获取逻辑的决策层,决定数据获取的策略
  3. ViewModel:Activity与Reposity的媒介,可以保存数据不丢失(如避免旋转屏幕时数据丢失)
  4. 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;
    }

说明:

  1. @Entity:指定表名
  2. @PrimaryKey(autoGenerate = true):定义为主键并自增
  3. @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);
}

说明:

  1. @Query: 是 DAO 类中使用的主要注释。它允许您对数据库执行读/写操作,返回类型要正确,不然会报错
  2. @Insert:插入数据,可以返回插入数据的id/id数组
  3. @Delete:根据主键删除给定的实体
  4. @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简单便捷许多,用来做内容缓存是个不错的选择,最后,也希望喜欢我文章的朋友们可以帮忙点赞、收藏、评论,也可以关注一下,如果有问题可以在评论区提出,谢谢大家的支持与阅读!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值