1、使用Room创建数据库
Jetpack下使用Room创建数据库,相较于之前使用DBhelper创建,更像是平常创建一个对象一样简单,使用标记让大部分代码可以有IDE自动完成
2、创建数据库存储对象
相当于DBhelper中创建一个表,将之前的数据库语句具象成一个对象,就如同创建普通对象一样,只需要在对象前加一个标记 @Entity
(实体),该对象会自动被识别,对象中的每个属性相当于数据库中的一个个字段,使用标记就可以声明主键等等;
1、创建一个实体对象
@Entity
public class People {
//主键自增加
@PrimaryKey(autoGenerate =true)
private int _id;
@ColumnInfo(name="name")
private String name="";
@ColumnInfo(name = "college")
private String college="未设置";
@ColumnInfo(name = "age")
private int age=19;
}
其中@PrimaryKey(autoGenerate =true)
即声明该字段为主键,并且自增,@ColumnInfo(name = "xxx")
是给字段在数据库中的命名,如果不加默认会以变量名作为字段明;
2、书写对象内的一些方法
这里就和构造普通的对象一样,可以在实体对象中定义不同的方法调用,例如构造方法、GET/SET、toString……
示例:
public People(String name) {
this.name = name;
}
public People(String name, String college) {
this.name = name;
this.college = college;
}
public int get_id() {
return _id;
}
public void set_id(int _id) {
this._id = _id;
}
public String getName() {
return name;
}
……
3、创建操作接口
***相当于进行Provider操作*有了对象实体,还需要定义操作接口,即Romm中的@Dao
(database access object–数据库访问对象),用于对数据库进行操作,同样利用 标记 简化了数据库操作
1、增
使用@Insert
标记,并且传入之前定义的对象实体即可:
@Insert
void InsertPeo(People... people);
注意:其中People...
表示可以传入一个或多个相同类型的参数
2、删
同样使用@Delete
标记加上实体对象即可,相当于简化了之前where语句,直接将一个对象作为参数,只要符合该对象就可以对其进行删除操作:
@Delete
int deletePeo(People... people);
3、改
利用@Update
语句传入对象即可进行操作:
默认按照主键修改
@Update
int updatePeo(People... people);
4、查和其他操作
和上述增、删、改的操作不同,Query可以除了查询做更多的事情,它更像是一个条件操作,同样利用@Query()
标记进行书写,与此不同的()类需要写入SQL语句,从而执行相应的操作:
@Query("SELECT * FROM People")
List<Word> getPeoAll();
@Query("DELETE FROM People")
void delAllPeo();
如上,分别是查询全部、删除全部的操作,举一反三可以定义更多的操作;
创建抽象类进行操作
这一步相当于DBhelper中的创建数据库,创建一个抽象方法并继承RoomDatabase
利用@Database()
标记进行数据库的创建操作,其中包含几个参数,一个是entities
——即之前创建的实体,这一步相当于告知在本数据库放入那些表,version
即字面意思,数据库的版本,用于后续升级使用:
@Database(entities = {People.class},version = 1)
public abstract class PeopleDatabase extends RoomDatabase {
public abstract PeopleDao getPeoDao();
}
以上所有使用标记部分,均相当于抽象类,IDE会生成相应的mpl代码,可以使用IDE中的方法实现功能查看相应的实现代码;
在程序中访问数据库
1、创建数据库访问对象
利用Room方法生成一个之前定义的抽象类,以才来构造数据库的访问:
peopleDatabase=Room.databaseBuilder(this,PeopleDatabase.class,"People")
.build();
其中构建的三个参数分别是:
- 上下文对象
- 数据库抽象类
- 数据库名称
需要说明的是该方法因为是耗时操作,如果直接在UI线程中运行会报错,此时需要使用allowMainThreadQueries()
方法构造,强制允许在UI线程中运行;
2、创建数据库操作方法
直接从刚刚获得的数据库对象中调用之前创建的getDao
方法获取数据库操作接口:
peopleDao=peopleDatabase.getPeoDao();
3、增删改查
分别调用之前在Dao中定义的方法并传入相应参数即可实现增删改查:
//增加
peopleDao.InsertPeo(new People("冷朴承","数学与计算机学院"));
peopleDao.InsertPeo(new People("小云快飞","数计学院")
,new People("玲儿"));
People people=new People("林儿");
people.setCollege("药学院");
people.setAge(21);
peopleDao.InsertPeo(people);
//删除
People people=new People();
people.set_id(4);
peopleDao.deletePeo(people);
//修改
People people=new People("承儿","数学与计算机学院");
people.set_id(4);
peopleDao.updatePeo(people);
//查询
List<People> peopleList=peopleDao.getPeoAll();
String str="";
for(People people:peopleList){
str+=people.get_id()+":"+people.getName()+"--"+people.getCollege()
+"_age:"+people.getAge()+"\n";
}
textView.setText(str);
可以看到之前的People...
在这里可以传入多个参数或者一个参数,或者是数组链表等;
使用AsyncTask
AsyncTask
是一个在UI下的异步线程,同时可以更新界面上的显示,用法是定义一个继承于AsyncTask的方法:
static class InsertAsyncTask extends AsyncTask<People,Void ,Void>{}
其中三个参数分别是,开始时的对象,进行中的对象,结束时候的对象,其中主要应用三个方法
1、线程开始前
复写onPreExecute
方法可以在线程开始前执行,例如用它打开一个滚轮或者进度条,告知界面这是耗时操作:
//开始前
@Override
protected void onPreExecute() {
super.onPreExecute();
}
2、线程初始化
复写doInBackground
方法,即表示线程启动进行什么操作,如下案例就是进行插入操作;
//做什么事情
@Override
protected Void doInBackground(People... peoples) {
peoDao.insertPeo(peoples);
return null;
}
该方法是
AsyncTask
必须复写的方法,表示的任务是该线程的主任务
3、线程正在进行时
复写onProgressUpdate
即线程正在运行时候的回调,例如在这里动态滚动进度条等等;
//当进度发生更新时候的操作
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
4、线程工作完成时
复写onPostExecute
方法即可以在线程完成即将结束时操作
//线程完成时操作,一般将结果带回给主线程
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
调用AsyncTask
new UpdateAsyncTask(peopleDao).execute(people);
优化数据库
1、静态DataBase
使用synchronized
其只有唯一一个对象节约资源占用
@Database(entities = {People.class},version = 1)
public abstract class PeopleDatabase extends RoomDatabase {
private static PeopleDatabase INSTANCE;
static synchronized PeopleDatabase getDatabase(Context context){
if(INSTANCE ==null){
INSTANCE= Room.databaseBuilder(context.getApplicationContext(),PeopleDatabase.class,"peo_data")
.allowMainThreadQueries()
.build();
}
return INSTANCE;
};
public abstract PeopleDao getPeoDao();
}
2、动态LiveData
将所有和Query相关的返回为LiveData,既可对数据进行观察,方便后续的其他操作
@Query("SELECT * FROM People")
LiveData<List<People>> getPeoAll();
添加观察者
LiveData<List<People>> allPeos;
allPeos=peopleDao.getPeoAll();
allPeos.observe(this, new Observer<List<People>>() {
@Override
public void onChanged(List<People> people) {
StringBuilder str= new StringBuilder();
for(People peo:people){
str.append(peo.get_id()).append(":").append(peo.getName()).append("_").append(peo.getCollege()).append("_").append(peo.getAge()).append("\n");
}
textView.setText(str.toString());
}
});
就可以在数据发生改变时,动态的修改UI界面
3、ViewModel解耦Activity
需要先在
dependencies
中添加依赖
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
否则生成ViewMode需要传入另一个参数
1、创建ViewMode
为了后续维护和扩展便捷,将数据操作于界面分离,故创建ViewModel对象来管理界面上的数据
继承
AndroidViewModel
可以让系统管理生命周期
public class PeopleViewModel extends AndroidViewModel {
LiveData<List<People>> getAllPeos() {
return repository.getAllPeos();
}
……
}
在Activity
中调用ViewModelProvider
方法来构造
PeopleViewModel peopleViewModel= new ViewModelProvider(this).get(PeopleViewModel.class);
2、创建Repository工具类进一步解耦
刚刚创建的ViewModel中分离了界面与数据,但是其中还是会掺和着与启动线程、获取数据相关的操作,为了进一步的扩展,将数据库操作完全与界面解耦,定义一个Repository
类,封装所有的数据库操作
class PeopleRepository {
private LiveData<List<People>> allPeos;
private PeopleDao peopleDao;
PeopleRepository(Context context){
PeopleDatabase peopleDatabase=PeopleDatabase.getDatabase(context.getApplicationContext());
peopleDao=peopleDatabase.getPeoDao();
allPeos=peopleDao.getPeoAll();
}
LiveData<List<People>> getAllPeos() {
return allPeos;
}
void insertPeo(People... people){
new InsertAsyncTask(peopleDao).execute(people);
}
void delPeo(People... people){
new DeleteAsyncTask(peopleDao).execute(people);
}
void updatePeo(People... people){
new UpdateAsyncTask(peopleDao).execute(people);
}
void delAll(){
new DelAllAsyncTask(peopleDao).execute();
}
//一个异步线程 三个参数 对象,进度,结果
public static class InsertAsyncTask extends AsyncTask<People,Void ,Void>{
private PeopleDao peoDao;
InsertAsyncTask(PeopleDao peoDao) {
this.peoDao=peoDao;
}
@Override
protected Void doInBackground(People... peoples) {
peoDao.insertPeo(peoples);
return null;
}
}
static class UpdateAsyncTask extends AsyncTask<People,Void ,Void>{
private PeopleDao peoDao;
UpdateAsyncTask(PeopleDao peoDao) {
this.peoDao=peoDao;
}
@Override
protected Void doInBackground(People... peoples) {
peoDao.updatePeo(peoples);
return null;
}
}
static class DeleteAsyncTask extends AsyncTask<People,Void ,Void>{
private PeopleDao peoDao;
DeleteAsyncTask(PeopleDao peoDao) {
this.peoDao=peoDao;
}
@Override
protected Void doInBackground(People... peoples) {
peoDao.deletePeo(peoples);
return null;
}
}
//void即缺省
static class DelAllAsyncTask extends AsyncTask<Void,Void ,Void> {
private PeopleDao peoDao;
DelAllAsyncTask(PeopleDao peoDao) {
this.peoDao=peoDao;
}
@Override
protected Void doInBackground(Void... voids) {
peoDao.delAllPeo();
return null;
}
}
}
3、ViewModel回调Repository接口
public class PeopleViewModel extends AndroidViewModel {
private PeopleRepository repository;
public PeopleViewModel(@NonNull Application application) {
super(application);
repository=new PeopleRepository(application);
}
LiveData<List<People>> getAllPeos() {
return repository.getAllPeos();
}
void insertPeo(People... people){
repository.insertPeo(people);
}
void delPeo(People... people){
repository.delPeo(people);
}
void updatePeo(People... people){
repository.updatePeo(people);
}
void delAll(){
repository.delAll();
}
}
4、在Activity中使用案例
public class MainActivity extends AppCompatActivity {
TextView textView;
Button button5, button6, button7, button8;
//创建ViewModel实例
PeopleViewModel peopleViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
peopleViewModel = new ViewModelProvider(this).get(PeopleViewModel.class);
textView = findViewById(R.id.textView);
peopleViewModel.getAllPeos().observe(this, new Observer<List<People>>() {
@Override
public void onChanged(List<People> people) {
StringBuilder str = new StringBuilder();
for (People peo : people) {
str.append(peo.get_id()).append(":").append(peo.getName()).append("_").append(peo.getCollege()).append("_").append(peo.getAge()).append("\n");
}
textView.setText(str.toString());
}
});
button5 = findViewById(R.id.button5);
button5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
People people = new People("林儿");
people.setCollege("药学院");
people.setAge(21);
peopleViewModel.insertPeo(new People("冷朴承", "数学与计算机学院")
, new People("小云快飞", "数计学院")
, new People("玲儿"), people);
}
});
button6 = findViewById(R.id.button6);
button6.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
People people = new People();
people.set_id(4);
peopleViewModel.delPeo(people);
}
});
button7 = findViewById(R.id.button7);
button7.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
People people = new People("承儿", "数学与计算机学院");
people.set_id(4);
peopleViewModel.updatePeo(people);
}
});
button8 = findViewById(R.id.button8);
button8.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
peopleViewModel.delAll();
}
});
}
}
至此一个使用Room的数据库定义到访问完成,虽然相比于原生的getContentResolver()
可能看起来类变多了,但实际在书写上和归类上都简化了,而且将数据和界面解耦,让后续的维护和扩展更加便捷,特别是将数据表具象为一个实体对象,可以像创建普通的对象一样使用,极大的简化了操作