概述
数据库升级
数据库升级一般体现在表中字段类型有变化,亦或者字段有增删,实际体现为实体类属性发生增减或类型变化,这就需要数据库进行升级。
1,以Student增加address字段为例
@Database(entities = {Student.class}, version = 2)
//当前要安装/升级的app的数据库版本号,如果之前安装的版本小于当前版本号,涉及数据库表结构变动则需考虑升级
public abstract class AppDatabase extends RoomDatabase {
private static final String DATABASE_NAME = "my_db";
private static AppDatabase databaseInstance;
public static AppDatabase getInstance(Context context) {
if (databaseInstance == null) {
synchronized (AppDatabase.class) {
if (databaseInstance == null) {
databaseInstance = Room
.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME)
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)//添加版本从1到2的支持类(迁移类),也可以再添加2到3,或者3到4 如果升级到的版本没有相关支持类,就会崩
//Caused by: java.lang.IllegalStateException: A migration from 1 to 3 was required but not found.
//如果需要1升到3,但没有1到3的迁移文件,有1到2和2到3的文件也是可以的,会按顺序先后执行
.fallbackToDestructiveMigration()//为了处理没有版本迁移类会崩的问题,增加兼容处理,但会导致数据库原有数据丢失
.build();
Log.e("ME", "当前数据库版本号=" + databaseInstance.getOpenHelper().getReadableDatabase().getVersion());
}
}
}
return databaseInstance;
}
public abstract StudentDao studentDao();
static final int V_OLD = 1;
static final int V_NEW = 2;
static final Migration MIGRATION_1_2 = new Migration(V_OLD, V_NEW) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
Log.e("ME", "12升级前数据库版本号=" + database.getVersion());
//Student类增加了address字段
database.execSQL("ALTER TABLE student"
+ " ADD COLUMN address TEXT ");
}
};
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
Log.e("ME", "23升级前数据库版本号=" + database.getVersion());
//TODO 升级数据库要做的操作
}
};
}
Student的结构发生变化时需要更改数据库版本号,并进行相关升级操作,否则直接报错无法运行
@Entity(tableName = "student")
public class Student
{
...
@ColumnInfo(name = "address", typeAffinity = ColumnInfo.TEXT)
public String address;
}
小结:一般表结构发生变化通常功能增加而增加字段,一般的操作为创建临时表,复制数据,删除旧表,重命名新表为旧表名称,也可以更加简洁直接修改表结构同时添加表字段。
Room搭配Livedata使用
Livedata在于实时监听数据,Room与Livedata的搭配使用就是监听数据库的变化咯。
具体看代码
1,在数据访问对象Dao中添加返回Livedata<List<T>>
@Dao
public interface StudentDao {
...
@Query("SELECT * FROM student")
LiveData<List<Student>> getAllStudentList();
}
2,考虑到使用Database需要传入上下文,使用AndroidViewModel
public class RoomLiveDataViewModel extends AndroidViewModel {
private AppDatabase appDatabase;
private LiveData<List<Student>> liveDataStudent;
public RoomLiveDataViewModel(@NonNull Application application) {
super(application);
appDatabase = AppDatabase.getInstance(application);
liveDataStudent = appDatabase.studentDao().getAllStudentList();
}
public LiveData<List<Student>> getLiveDataStudent() {
return liveDataStudent;
}
}
3,在页面上引用Viewmodel,并监听数据变化
public class RoomLiveDataActivity extends AppCompatActivity {
private ArrayList<Student> students = new ArrayList<>();
private StudentAdapter adapter = null;
private RecyclerView recyclerview;
private StudentDao studentDao;
ExecutorService executor = Executors.newSingleThreadExecutor();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main7);
recyclerview = this.findViewById(R.id.recyclerview);
adapter = new StudentAdapter(students);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerview.setLayoutManager(linearLayoutManager);
recyclerview.setAdapter(adapter);
studentDao = AppDatabase.getInstance(this).studentDao();
RoomLiveDataViewModel viewModel = new ViewModelProvider(this).get(RoomLiveDataViewModel.class);
viewModel.getLiveDataStudent().observe(this, new Observer<List<Student>>() {
@Override
public void onChanged(List<Student> ss) {//这里犯了个低级操作,返回的默认是students,和定义的列表同名了,导致数据没有变化
Log.e("ME","有变化");//此时是在主线程
students.clear();
students.addAll(ss);
adapter.notifyDataSetChanged();
}
});
}
public void addClick(View view) {
executor.submit(new Runnable() {
@Override
public void run() {
Student st_max = studentDao.getMaxIdStudent();
Log.e("ME", "max=" + (st_max == null ? "null" : st_max.toString()));
Student student = new Student("sun" + (st_max == null ? 20 : st_max.id + 1), 21);
studentDao.insertStudent(student);
}
});
}
public void deleteOneClick(View view) {
executor.submit(new Runnable() {
@Override
public void run() {
Student st_min = studentDao.getMinIdStudent();
Log.e("ME", "mix=" + (st_min == null ? "null" : st_min.toString()));
if (st_min != null) {
studentDao.deleteStudent(st_min);
}
}
});
}
public void updateClick(View view) {
executor.submit(new Runnable() {
@Override
public void run() {
Student st_max = studentDao.getMaxIdStudent();
Log.e("ME", "max=" + (st_max == null ? "null" : st_max.toString()));
if (st_max != null) {
st_max.age++;
studentDao.updateStudent(st_max);
}
}
});
}
public void clearAllClick(View view) {
executor.submit(new Runnable() {
@Override
public void run() {
studentDao.deleteAllStudent(studentDao.getStudentList());
}
});
}
public static void startActivity(Context context) {
context.startActivity(new Intent(context, RoomLiveDataActivity.class));
}
}
在页面上已经没有使用查询全部数据的接口了,全程通过监听数据库变化并加载变化后的数据从而实现数据的实时同步,不用在数据发生变化后再单独开线程去查询数据了,而且监听的数据变化回调已经自动切换到主线程了,这就很nice了。
另外,Room提供了直接读取外部数据库文件的接口createFromAsset()和createFromFile(),直接在创建数据库时调用接口并传入相关数据库文件地址(asset或者sd卡,或者网络保存到sd卡),这样就实现了数据库的预填充,功能简单,不做过多讨论。至此,Room的相关操作已经完成,源码不打算做详细分析了。
Android-Jetpack代码位置:github
最后
以上就是奋斗帽子为你收集整理的Jetpack学习-10-Room升级及搭配Livedata使用的全部内容,希望文章能够帮你解决Jetpack学习-10-Room升级及搭配Livedata使用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复