Android Jetpack之Room

Room是在Sqlite数据的一个抽象层,拥有更强大的数据访问能力。

导入依赖:

1
2
3
4
5
6
7
8
9
10
def room_version = "2.1.0-alpha06"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"

// kotlin扩展和协程支持
implementation "androidx.room:room-ktx:$room_version"
//RxJava 支持库
implementation "androidx.room:room-rxjava2:$room_version"
// 可选 - Guava 的支持库
implementation "androidx.room:room-guava:$room_version"

下面开始使用

第一步创建实体类

假如我们有一个用户表,每个用户的实体就是表中的一列

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
public class User {

@PrimaryKey
@NonNull
@ColumnInfo(name = "id_")
public int id;

public String name;
public String password;
public String school;

}
  • @Entity: 代表一个表中的实体,默认类名就是表名,如果不想使用类名作为表名,可以给注解添加表名字段@Entity(tableName = "user_table")
  • @PrimaryKey: 每个实体都需要自己的主键
  • @NonNull 表示字段,方法,参数返回值不能为空
  • @ColumnInfo(name = “lastname”) 如果希望表中字段名跟类中的成员变量名不同,添加此字段指明

第二步创建DAO

  • DAO是数据访问对象,指定SQL查询,并让他与方法调用相关联。
  • DAO必须是一个接口或者抽象类。
  • 默认情况下,所有的查询都必须在单独的线程中执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Dao
public interface UserDao {
@Insert
void insert(User user);

@Query("select * from user")
List<User> getUserList();

@Query("delete from user")
void deleteAll();

@Update
void updateUsers(User... users);
}
  • 创建一个接口UserDao
  • 给它添加注解@Dao,表名它是Room的一个查询类
  • 声明一个插入用户的方法insert,并给它添加注解@Insert,不用提供任何SQL语句
  • 声明一个删除全部的方法,deleteAll(),删除方法没有便捷方法,需要使用@Query注解,并且提供相应的SQL语句delete from user
  • 声明一个getUserList方法来查询所有的用户,这个也没有便捷方法,,需要使用@Query注解,并且提供相应的SQL语句select * from user

第三步添加Database

Room是SQLite数据库之上的数据库层,可以让我们轻松的使用系统原始API:SQLiteOpenHelper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Database(entities = {User.class},version = 1)
public abstract class UserRoomDatabase extends RoomDatabase {
public abstract UserDao userDao();

public static UserRoomDatabase instance;

public static UserRoomDatabase getInstance(Context context){
if(instance == null){
synchronized (UserRoomDatabase.class){
if(instance == null){
instance = Room.databaseBuilder(context.getApplicationContext(),UserRoomDatabase.class
,"user_database").build();
}
}
}
return instance;
}
}

  • 创建一个抽象类继承自RoomDatabase
  • 给他添加一个注解@Database表名它是一个数据库,注解有两个参数第一个是数据库的实体,它是一个数组,可以传多个,当数据库创建的时候,会默认给创建好对应的表,第二个参数是数据库的版本号
  • 定义跟数据库一起使用的相关的DAO类
  • 创建一个UserRoomDatabase的单例,防止同时打开多个数据库的实例
  • 使用Room提供的数据库构建器来创建该实例,第一个参数application,第二个参数当前数据库的实体类,第三个参数数据库的名字

第四步开始使用

前面三步主要步骤写完了,现在就可以开始使用了,使用的时候,为了让Activity中代码简洁,创建一个UserRepository类来管理这个数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public class UserRepository {

private UserDao mUserDao;

private List<User> allUser;

public UserRepository(Application application) {
//UserRoomDatabase db = UserRoomDatabase.getInstance(application);
//mUserDao = db.userDao();
//allUser = mUserDao.getUserList();
//使用ViweModel可以直接使用上面注释了的
new InitThread(application).start();
}

public List<User> getAllUser() {
return allUser;
}

public void deleteAll(){
new DeleteAsyncTask(mUserDao).execute();
}

public void update(User user){
new UpdateAsyncTask(mUserDao).execute(user);
}

public void insert(User user){
new InsertAsyncTask(mUserDao).execute(user);
}

private class InitThread extends Thread{
Application application;
InitThread(Application application){
this.application = application;
}
@Override
public void run() {
UserRoomDatabase db = UserRoomDatabase.getInstance(application);
mUserDao = db.userDao();
allUser = mUserDao.getUserList();
}
}
//更新
private static class UpdateAsyncTask extends AsyncTask<User, Void, Void> {

private UserDao mAsyncTaskDao;

UpdateAsyncTask(UserDao dao) {
mAsyncTaskDao = dao;
}

@Override
protected Void doInBackground(final User... params) {
mAsyncTaskDao.updateUsers(params[0]);
return null;
}
}
//插入
private static class InsertAsyncTask extends AsyncTask<User, Void, Void> {

private UserDao mAsyncTaskDao;

InsertAsyncTask(UserDao dao) {
mAsyncTaskDao = dao;
}

@Override
protected Void doInBackground(final User... params) {
mAsyncTaskDao.insert(params[0]);
return null;
}
}
//删除
private static class DeleteAsyncTask extends AsyncTask<Void, Void, Void> {

private UserDao mAsyncTaskDao;

DeleteAsyncTask(UserDao dao) {
mAsyncTaskDao = dao;
}
@Override
protected Void doInBackground(Void... voids) {
mAsyncTaskDao.deleteAll();
return null;
}
}
}

这个类的作用就是初始化数据库和响应的DAO类,对外提供插入、查询等方法。

注意:数据库的创建,表的插入和删除操作,Room会强制要求在非UI线程中使用,否则会崩溃。

在Activity中初始化UserRepository之后,就可以进行相关的操作了

使用LiveData和ViewModel

当数据变化的时候,LiveData可以观察到数据的变化,可以让我们实时更新UI

ViewModel可以更好的保存Activity中的数据,比如屏幕旋转的时候数据不会丢失

ViewModel与Room和LiveData一起工作可以替换以前的loader。ViewModel确保数据在设备配置更改后仍然存在。当数据库发生更改时,Room会通LiveData,而LiveData反过来又用修改后的数据更的UI。

将DAO中的查询更改为下面

1
2
@Query("select * from user")
LiveData<List<User>> getUserList();

然后创建ViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class UserViewModel extends AndroidViewModel {
private LiveData<List<User>> mUsers;
private UserRepository mRepository;

public UserViewModel(Application application) {
super(application);
mRepository = new UserRepository(application);
mUsers = mRepository.getAllUser();
}

public LiveData<List<User>> getUsers(){
return mUsers;
}

public void insertUser(User user){
mRepository.insert(user);
}

public void deleteAll(){
mRepository.deleteAll();
}

public void update(User user){
mRepository.update(user);
}
}

我们单独使用LiveData的时候,都是使用MutableLiveData,当与Room一块使用的时候只能使用LiveData。

Activity中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class RoomActivity extends AppCompatActivity {
List<User> mUsers = new ArrayList<>();
UserRepository mRepository;
MyAdapter mAdapter;
int index = 0;
private UserViewModel mViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_room);
// mRepository = new UserRepository(getApplication());
// mUsers = new ArrayList<>();
RecyclerView recyclerView = findViewById(R.id.recycleview);
LinearLayoutManager manager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(manager);
mAdapter = new MyAdapter(mUsers,this);
recyclerView.setAdapter(mAdapter);

mViewModel = ViewModelProviders.of(this).get(UserViewModel.class);

mViewModel.getUsers().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(List<User> users) {
mUsers.clear();
mUsers.addAll(users);
mAdapter.notifyDataSetChanged();
}
});

}

public void insert(View view) {
User user = new User();
user.id =index;
user.name = "张三"+ Math.random()*100;
user.school = "北大"+ Math.random()*100;
user.password = "123"+ Math.random()*100;
// mRepository.insert(user);
mViewModel.insertUser(user);
index++;
}

public void query(View view) {
// List<User> allUser = mRepository.getAllUser();
// mUsers.addAll(allUser);
// mAdapter.notifyDataSetChanged();
}

public void deleteAll(View view) {
mViewModel.deleteAll();
}

public void update(View view) {
User user = new User();
user.id =0;
user.name = "张三"+ Math.random()*100;
user.school = "北大"+ Math.random()*100;
user.password = "123"+ Math.random()*100;
mViewModel.update(user);
}
}

数据库升级

当数据库中的表或者表中的字段有变化的时候,我们需要升级数据的版本,这个时候,我们不希望现在数据库中的数据丢失

Room提供了相应的类(Migration)来完成数据库的迁移,需要传入一个旧版本和新版本,比如现在在user表中新加一个age字段

  1. 在User类中新加一个字段
1
public int age;
  1. 编写Migration类,编写sql更改数据库
1
2
3
4
5
6
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("alter table user add age INTEGER NOT NULL default 0");
}
};
  1. 更改数据库的版本由1变成2
1
2
@Database(entities = {User.class},version = 2,exportSchema = false)
public abstract class UserRoomDatabase extends RoomDatabase {}
  1. 更改数据库的创建方法
1
2
3
4
instance = Room.databaseBuilder(context.getApplicationContext(),UserRoomDatabase.class
,"user_database")
.addMigrations(MIGRATION_1_2)
.build();

然后重新运行程序就可以看到age字段已经加到表里了。

addMigrations方法,里面可以接收多个参数,比如现在只是编写了版本1-2的升级方法MIGRATION_1_2,假如我们还有2-3版本的还可以编写一个MIGRATION_2_3,添加到后面。

更新数据库的版本


OK,到这里Room的简单使用就完成啦。下面来看看它的源码吧

前面我们知道数据库的创建是从Room.databaseBuilder(...).build();方法开始,很明显看出来这是通过建造者模式创建出来的。传入一些参数到RoomDatabase.Builder中,最终的创建方法肯定就是在build中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  public T build() {

......

if (mFactory == null) {
mFactory = new FrameworkSQLiteOpenHelperFactory();
}
//数据库配置
DatabaseConfiguration configuration =
new DatabaseConfiguration(
mContext,
mName,
mFactory,
mMigrationContainer,
mCallbacks,
mAllowMainThreadQueries,
mJournalMode.resolve(mContext),
mQueryExecutor,
mTransactionExecutor,
mMultiInstanceInvalidation,
mRequireMigration,
mAllowDestructiveMigrationOnDowngrade,
mMigrationsNotRequiredFrom);
//前面创建的UserRoomDatabase是个抽象类,编译期间会生成对应的实现类UserRoomDatabase_Impl,获取UserRoomDatabase的实现类
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
//初始化数据库
db.init(configuration);
return db;
}
}

创建了一个SQLiteOpenHelper的工厂类FrameworkSQLiteOpenHelperFactory

1
2
3
4
5
6
7
public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
@Override
public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
return new FrameworkSQLiteOpenHelper(
configuration.context, configuration.name, configuration.callback);
}
}

这个工厂方法可以创建一个FrameworkSQLiteOpenHelper,它是SupportSQLiteOpenHelper接口的实现类。

1
2
3
4
5
6
7
class FrameworkSQLiteOpenHelper implements SupportSQLiteOpenHelper {
private final OpenHelper mDelegate;

FrameworkSQLiteOpenHelper(Context context, String name,
Callback callback) {
mDelegate = createDelegate(context, name, callback);
}

可以看到在其构造方法中创建了一个代理类OpenHelper

1
static class OpenHelper extends SQLiteOpenHelper {......}

OpenHelper继承自系统的SQLiteOpenHelper,它用来监听数据库的创建(onCreate)升级(onUpgrade)等操作。然后回调给RoomOpenHelper来处理。

回到build()方法中,将数据库的配置封装到DatabaseConfiguration中,然后获取我们之前写的抽象类UserRoomDatabase的一个实现类,这个实现类是注解器在编译期间自动创建的。

位置在:build->generated->source->apt->debug->你的包名中找到。最后初始化数据库。怎么创建的可以去查一下编译时注解的原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
//包名
final String fullPackage = klass.getPackage().getName();
//全名
String name = klass.getCanonicalName();
final String postPackageName = fullPackage.isEmpty()
? name
: (name.substring(fullPackage.length() + 1));
//拼成UserRoomDatabase_Impl
final String implName = postPackageName.replace('.', '_') + suffix;
//noinspection TryWithIdenticalCatches
try {
//通过反射找到生成的类,然后实例化
@SuppressWarnings("unchecked")
final Class<T> aClass = (Class<T>) Class.forName(
fullPackage.isEmpty() ? implName : fullPackage + "." + implName);
return aClass.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("cannot find implementation for "
+ klass.getCanonicalName() + ". " + implName + " does not exist");
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot access the constructor"
+ klass.getCanonicalName());
} catch (InstantiationException e) {
throw new RuntimeException("Failed to create an instance of "
+ klass.getCanonicalName());
}
}

上面的方法就是根据我们传入的数据库的类名,拼接出Room编译器给自动生成的实现类的名字,然后通过反射找到这个类并实例化返回。

下面看一下这个自动生成的类UserRoomDatabase_Impl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
public final class UserRoomDatabase_Impl extends UserRoomDatabase {
private volatile UserDao _userDao;

@Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(2) {
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id_` INTEGER NOT NULL, `name` TEXT, `password` TEXT, `school` TEXT, `age` INTEGER NOT NULL, PRIMARY KEY(`id_`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
_db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"c59ead1e532b11ea2062c3f5e814a66b\")");
}

@Override
public void dropAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("DROP TABLE IF EXISTS `User`");
}

@Override
protected void onCreate(SupportSQLiteDatabase _db) {
if (mCallbacks != null) {
for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
mCallbacks.get(_i).onCreate(_db);
}
}
}

@Override
public void onOpen(SupportSQLiteDatabase _db) {
mDatabase = _db;
internalInitInvalidationTracker(_db);
if (mCallbacks != null) {
for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
mCallbacks.get(_i).onOpen(_db);
}
}
}

@Override
public void onPreMigrate(SupportSQLiteDatabase _db) {
DBUtil.dropFtsSyncTriggers(_db);
}

@Override
public void onPostMigrate(SupportSQLiteDatabase _db) {
}

@Override
protected void validateMigration(SupportSQLiteDatabase _db) {
final HashMap<String, TableInfo.Column> _columnsUser = new HashMap<String, TableInfo.Column>(5);
_columnsUser.put("id_", new TableInfo.Column("id_", "INTEGER", true, 1));
_columnsUser.put("name", new TableInfo.Column("name", "TEXT", false, 0));
_columnsUser.put("password", new TableInfo.Column("password", "TEXT", false, 0));
_columnsUser.put("school", new TableInfo.Column("school", "TEXT", false, 0));
_columnsUser.put("age", new TableInfo.Column("age", "INTEGER", true, 0));
final HashSet<TableInfo.ForeignKey> _foreignKeysUser = new HashSet<TableInfo.ForeignKey>(0);
final HashSet<TableInfo.Index> _indicesUser = new HashSet<TableInfo.Index>(0);
final TableInfo _infoUser = new TableInfo("User", _columnsUser, _foreignKeysUser, _indicesUser);
final TableInfo _existingUser = TableInfo.read(_db, "User");
if (! _infoUser.equals(_existingUser)) {
throw new IllegalStateException("Migration didn't properly handle User(com.chs.androiddailytext.jetpack.User).\n"
+ " Expected:\n" + _infoUser + "\n"
+ " Found:\n" + _existingUser);
}
}
}, "c59ead1e532b11ea2062c3f5e814a66b", "c0809d515e95fc9eec52ad8880ec6aae");
final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
.name(configuration.name)
.callback(_openCallback)
.build();
final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
return _helper;
}

@Override
protected InvalidationTracker createInvalidationTracker() {
final HashMap<String, String> _shadowTablesMap = new HashMap<String, String>(0);
HashMap<String, Set<String>> _viewTables = new HashMap<String, Set<String>>(0);
return new InvalidationTracker(this, _shadowTablesMap, _viewTables, "User");
}

@Override
public void clearAllTables() {
super.assertNotMainThread();
final SupportSQLiteDatabase _db = super.getOpenHelper().getWritableDatabase();
try {
super.beginTransaction();
_db.execSQL("DELETE FROM `User`");
super.setTransactionSuccessful();
} finally {
super.endTransaction();
_db.query("PRAGMA wal_checkpoint(FULL)").close();
if (!_db.inTransaction()) {
_db.execSQL("VACUUM");
}
}
}

@Override
public UserDao userDao() {
if (_userDao != null) {
return _userDao;
} else {
synchronized(this) {
if(_userDao == null) {
_userDao = new UserDao_Impl(this);
}
return _userDao;
}
}
}
}

  • createOpenHelper方法,创建SupportSQLiteOpenHelper的实现类FrameworkSQLiteOpenHelper,前面我们知道他的构造方法中创建了一个代理类OpenHelper它继承自系统的SQLiteOpenHelper,这个就是我们如果不使用Room,而是自己使用系统提供的类操作数据库的时候需要创建的类,这里Room帮我们创建好了,这个方法是在前面build()方法中的 db.init(configuration)中调用的
  • createOpenHelper方法中创建了SupportSQLiteOpenHelper.Callback这个回调,并实现它的回调方法从代码中看到它是一个RoomOpenHelper。
  • OpenHelper监听系统回调,监听到之后会回调RoomOpenHelper的相关方法,RoomOpenHelper又会回调UserRoomDatabase_Impl中的相关方法。
  • onCreate()中Sql语句创建user表和room_master_table表,dropAllTables方法删除数据库,validateMigration方法实现数据库升级
  • userDao()方法创建UserDao_Impl的实例,这个UserDao_Impl也是注解处理器给我们自动生成的,这里面就是我们定义的UserDao中增删改查的真正实现的地方。

然后在看build()方法中的init方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void init(@NonNull DatabaseConfiguration configuration) {
mOpenHelper = createOpenHelper(configuration);
boolean wal = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
wal = configuration.journalMode == JournalMode.WRITE_AHEAD_LOGGING;
mOpenHelper.setWriteAheadLoggingEnabled(wal);
}
mCallbacks = configuration.callbacks;
mQueryExecutor = configuration.queryExecutor;
mTransactionExecutor = new TransactionExecutor(configuration.transactionExecutor);
mAllowMainThreadQueries = configuration.allowMainThreadQueries;
mWriteAheadLoggingEnabled = wal;
if (configuration.multiInstanceInvalidation) {
mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
configuration.name);
}
}
  • createOpenHelper(configuration)就是调用了UserRoomDatabase_Impl中的createOpenHelper方法。
  • mQueryExecutor 和 mTransactionExecutor 是两个线程池,查询和事物的线程池。这就是为啥前面例子中我们的查询方法不用自己放到非UI线程中执行,而插入和更新方法却需要自己创建子线程执行了。

下面来看看升级的方法,前面我们知道OpenHelper这个代理类继承了系统的SQLiteOpenHelper,会监听系统的数据库相关事件,我们找到它中的升级方法

1
2
3
4
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
mMigrated = true;
mCallback.onUpgrade(getWrappedDb(sqLiteDatabase), oldVersion, newVersion);
}

mCallback就是我们在UserRoomDatabase_Impl中创建的RoomOpenHelper一路传进来的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
boolean migrated = false;
if (mConfiguration != null) {
//根据版本号查找对应的migrations
List<Migration> migrations = mConfiguration.migrationContainer.findMigrationPath(
oldVersion, newVersion);
if (migrations != null) {
//迁移之前的初始化工作
mDelegate.onPreMigrate(db);
//循环执行我们写的sql
for (Migration migration : migrations) {
migration.migrate(db);
}
//验证升级结果
mDelegate.validateMigration(db);
mDelegate.onPostMigrate(db);
updateIdentity(db);
migrated = true;
}
}
//如果没有执行我们的sql
if (!migrated) {
if (mConfiguration != null
&& !mConfiguration.isMigrationRequired(oldVersion, newVersion)) {
//删除清空表
mDelegate.dropAllTables(db);
mDelegate.createAllTables(db);
} else {
throw new IllegalStateException("A migration from " + oldVersion + " to "
+ newVersion + " was required but not found. Please provide the "
+ "necessary Migration path via "
+ "RoomDatabase.Builder.addMigration(Migration ...) or allow for "
+ "destructive migrations via one of the "
+ "RoomDatabase.Builder.fallbackToDestructiveMigration* methods.");
}
}
}

这里面的mDelegate就是我们在UserRoomDatabase_Impl中new出来的RoomOpenHelper.Delegate,并实现了它里面的方法,所以上面代码中调用Delegate最终都会到达UserRoomDatabase_Impl中的相关方法执行。

findMigrationPath方法根据版本号找到相应的migrations,前面使用中我们知道migrations中封装了我们升级数据库的sql语句。

循环执行我们的migrations,执行我们写的升级的sql语句,执行完之后验证是否升级成功。

如果没有执行找到需要执行的migrations ,并且mConfiguration.isMigrationRequired(oldVersion, newVersion)为false,就会清空数据库中所有的表。

1
2
3
4
5
6
7
public boolean isMigrationRequired(int fromVersion, int toVersion) {
final boolean isDowngrade = fromVersion > toVersion;
if (isDowngrade && allowDestructiveMigrationOnDowngrade) {
return false;
}
......
}

可以看到当旧版本大于新版本的时候或者allowDestructiveMigrationOnDowngrade为true的时候返回false。

allowDestructiveMigrationOnDowngrade这个标志位可以在数据库创建的时候指定

1
2
3
4
Room.databaseBuilder(context.getApplicationContext(),UserRoomDatabase.class
,"user_database")
.fallbackToDestructiveMigration()
.build();

加上它之后升级就会清空数据库中以前的数据。一般情况下我们都是希望保留数据的,所以需要些我们自己的Migration类,定义升级的sql。

OK结束

コメント

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×