最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面。
Android Architecture组件是Android Jetpack的一部分,它们是一组库,旨在帮助开发者设计健壮、可测试和可维护的应用程序,包含一下组件:
上述时Android Architecture所提供的架构组件,本文主要从使用和源码的角度分析Room组件。
Room是Google提供的一个ORM库。Room提供了三个主要的组件:
数据库的创建
@Database(entities = {User.class}, version = 1) // 注释
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao(); // 抽象方法
}
public static UserDataBase getInstance(Context context) {
if (userDataBase == null) {
synchronized (UserDataBase.class) {
if (userDataBase == null) {
userDataBase = Room.databaseBuilder(context.getApplicationContext()
, UserDataBase.class, "user_data").build();
}
}
}
return userDataBase;
}
定义实体数据:表示数据库中的表
@Entity(tableName = "userDataBase")
class User {
@PrimaryKey(autoGenerate = true) // 单个主键设置为自增长
public var id = 0
@ColumnInfo(name = "nameUser") // 定义列名
public var name: String? = null
}
@Entity(primaryKeys = ["id", "name"]) // 组合组件
@Entity(indices = [Index("nameUser"), Index(value = ["name"])]) // 创建索引
@Entity(indices = [Index("nameUser"), Index(value = ["name"] ,unique = true)]) //唯一索引
@Entity(foreignKeys = [ForeignKey(entity = User::class,
parentColumns = ["id"],
childColumns = ["user_id"])])
class Book {
@PrimaryKey
var bookId: Int = 0
var title: String? = null
@ColumnInfo(name = "user_id")
var userId: Int = 0
}
class Address {
public var street: String? = null
public var state: String? = null
public var city: String? = null
@ColumnInfo(name = "post_code")
public var postCode = 0
}
// 在User实体中引入Address
@Embedded
public var address: Address? = null
访问数据库
@Dao
public interface UserDao {
@Insert // 添加数据注解
void insertAll(User... users);
@Delete // 删除数据注解
void delete(User user);
}
@Insert
public fun inertUser(user: User) // 单个参数可以返回 long
@Insert
public fun insertUserList(array: Array) // 参数为集合可以返回long[]
val user = User()
user.name = "赵云 编号 = $number"
val address = Address()
address.street = "成都接头"
address.state = "蜀汉"
address.city = "常山"
address.postCode = 10010
user.address = address
userDao.inertUser(user) // 添加User
@Update
public fun update(user: User) // 可以让此方法返回一个int值,表示数据库中更新的行数
val user = User()
user.id = 1
user.name = "张翼德"
address.city = "涿郡"
.....
userDao.update(user)
@Delete
public fun delete(user: User) //可以返回一个int值,表示从数据库中删除的行数
val user = User()
user.id = 1 // 要删除的主键 id
userDao.delete(user)
@Query("SELECT * FROM user")
public fun selectAll(): Array // 查询所有数据
@Query("SELECT * FROM user WHERE name = :name")
public fun selectUser(name:String): Array // 条件查询
public class UserTuple{ // 1、根据要查询的字段创建POJO对象
@ColumnInfo(name = "name")
public var name: String? = null
@ColumnInfo(name = "city")
public var city: String? = null
}
@Query("SELECT name ,city FROM user") // 2、查询的结果会映射到创建的对象中
public List loadFullName();
val userList = userDao.loadFullName()
for (userTuple in userList) {
stringBuilder.append(userTuple.name)
.append(" ")
.append(userTuple.city)
.append("\n")
}
@Query("SELECT name ,street FROM user WHERE city IN (:cityArray)")
fun loadUserInCity(cityArray: Array): List
val userList = userDao.loadUserInCity(arrayOf("常山")) // 查询常山,只会出现赵云不会出现张翼德
@Query("SELECT name ,street FROM user WHERE city IN (:cityArray"))
fun loadUserInCityLive(cityArray: Array): LiveData>
private lateinit var liveData: LiveData> // 定义一个LiveData
get() {
return userDao.loadUserInCityLive(arrayOf("常山"))
}
val observer = Observer> { // 定义一个观察者
val stringBuilder = StringBuilder()
for (index in it!!.indices) {
val userTuple = it[index]
stringBuilder.append(userTuple.name)
.append(" ")
.append(userTuple.name)
.append(" \n")
}
tv_main_show.text = stringBuilder.toString()
}
liveData.observe(this, observer) // 注册观察者
运行结果:此时当添加数据时,UI会自动更新;
@Query("SELECT * FROM user WHERE id = :id LIMIT 1")
fun loadUserRxJava(id:Int) : Flowable
userDao.loadUserRxJava(4)
.subscribe(Consumer {
val stringBuilder = StringBuilder()
stringBuilder.append(it.id)
.append(" ")
.append(it.name)
.append(" \n")
tv_main_show.text = stringBuilder.toString()
})
fun loadUserCursor(id:Int) : Cursor
@Query("SELECT user.name AS userName, pet.name AS petName "
+ "FROM user, pet "
+ "WHERE user.id = pet.user_id")
static final Migration MIGRATION_1_2 = new Migration(1, 2) { //由1升级到版本2
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE book (id INTEGER , name TEXT )")
}
};
static final Migration MIGRATION_2_3 = new Migration(2, 3) { //由2升级到版本3
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE user ADD COLUMN strength INTEGER NOT NUll DEFAULT 0") //添加strength列
}
};
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();
升级完数据库后再次查询,结果显示数据库增加了strength列名:
Room提供了在原始类型和目标类型之间进行转换的功能,但不允许实体之间的对象引用,对于其他类型之间的使用需要自定义转换器
使用TypeConverter,它将自定义类转换为Room可以保留的已知类型,如:想保存Date类型,而Room无法持久化实例Date却可以实例long,因此提供和long的相互转换
public class Converters {
@TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
@TypeConverter
public static Long dateToTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}
@TypeConverters({Converters.class})
@Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")
List findUsersBornBetweenDates(Date from, Date to);
以上就是数据库Room的使用简介了,基本数据库的增删改查以及常见的设置都在其中了,下面我们来看看Room是如何实现这些过程的,从源码角度分析数据库。
数据库的创建和升级
Room数据库实例的创建由Room.databaseBuilder(context.applicationContext,RoomTestData::class.java, "Sample.db").build()开始的,从代码中看出时使用Builder模式创建DataBase,所以我们先看看RoomDatabase.Builde类
@NonNull
public Builder addMigrations(@NonNull Migration... migrations) { // 添加数据库版本升级信息
if (mMigrationStartAndEndVersions == null) {
mMigrationStartAndEndVersions = new HashSet<>();
}
for (Migration migration: migrations) {
mMigrationStartAndEndVersions.add(migration.startVersion);
mMigrationStartAndEndVersions.add(migration.endVersion);
}
mMigrationContainer.addMigrations(migrations);
return this;
}
private static final String DB_IMPL_SUFFIX = "_Impl"
。。。。。。
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX); // 创建DataBase实现类的实例
db.init(configuration); // 初始化数据库
static T getGeneratedImplementation(Class klass, String suffix) {
final String fullPackage = klass.getPackage().getName();
String name = klass.getCanonicalName();
final String postPackageName = fullPackage.isEmpty()
? name
: (name.substring(fullPackage.length() + 1)); // 获取类名
final String implName = postPackageName.replace('.', '_') + suffix; // 拼接类名
//noinspection TryWithIdenticalCatches
try {
@SuppressWarnings("unchecked")
final Class aClass = (Class) Class.forName(
fullPackage.isEmpty() ? implName : fullPackage + "." + implName); // 获取自动生成的类文件
return aClass.newInstance(); // 创建并返回实例
} catch (ClassNotFoundException e) {
。。。。。。
}
}
此处获取到的是系统根据注解自动创建的是实现类RoomDataBase_Impl,Room采用的是注解自动生成代码方式,根据@DataBase和@Dao的注解,自动生成这两个注解标记的实现类,系统创建类如下图:
public class RoomTestData_Impl extends RoomTestData {
private volatile UserDao _userDao;
......
@Override
public UserDao userDao() {
if (_userDao != null) {
return _userDao;
} else {
synchronized(this) {
if(_userDao == null) {
_userDao = new UserDao_Impl(this); // 创建并返回UserDao的实例
}
return _userDao;
}
}
}
}
从上面的代码中看出,系统自动创建了RoomTestData的实现类,并重写了抽象方法userDao(),在userDao()中使用单例的方式提供UserDao的实现类UserDao_Impl,UserDao_Impl的形成和RoomTestData_Impl的生成一样,在代码中从DataBase中调用userDao返回的就是UserDao_Impl的实例;
接着分析数据库的创建,在上面的代码中有一句数据库的初始化代码db.init(),在db.init()的方法中会调用RoomDataBase中的抽象方法createOpenHelper(),这里调用的是createOpenHelper()就是RoomTestData_Impl自动实现的方法:
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(3) {
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("CREATE TABLE IF NOT EXISTS `user` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `strength` INTEGER NOT NULL, `name` TEXT, `street` TEXT, `state` TEXT, `city` TEXT, `post_code` INTEGER)"); // 创建数据库
_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, \"8ece9a1581b767a0f460940849e9b463\")");
}
@Override
public void dropAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("DROP TABLE IF EXISTS `user`"); // 删除数据库
}
@Override
protected void validateMigration(SupportSQLiteDatabase _db) { // 处理数据库的版本升级
。。。。。。
}
}, "8ece9a1581b767a0f460940849e9b463", "061261cef54147a569851cbbb906c3be");
}
。。。。。。
return _helper;
}
上面的代码中执行一下操作:
上面SupportSQLiteOpenHelper.Callback 的实现类为RoomOpenHelper,下面一起看看RoomOpenHelper源码:
@Override
public void onCreate(SupportSQLiteDatabase db) {
updateIdentity(db);
mDelegate.createAllTables(db); // mDelegate为上面创建的RoomOpenHelper.Delegate实例
mDelegate.onCreate(db);
}
@Override
public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
if (mConfiguration != null) {
List migrations = mConfiguration.migrationContainer.findMigrationPath(
oldVersion, newVersion);
if (migrations != null) {
for (Migration migration : migrations) {
migration.migrate(db);
}
mDelegate.validateMigration(db); // 调用validateMigration方法处理数据库的更新
updateIdentity(db);
migrated = true;
}
}
}
@Override
public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
从上面代码中可以看出,在onCreate()方法中调用了mDelegate.createAllTables(db),这里的mDelegate就是上面创建RoomOpenHelper方法中第二个参数RoomOpenHelper.Delegate,所以这里就是在onCreate()中创建了数据库,在onUPgrade()中调用 mDelegate.validateMigration(db)完成数据库的升级,到这里数据库的创建和升级已经介绍完毕了,下面就一起看看Room是如何访问数据库的。
数据库的访问
private final RoomDatabase __db; // 传入的数据库
private final EntityInsertionAdapter __insertionAdapterOfUser; // 处理insert方法
private final EntityDeletionOrUpdateAdapter __deletionAdapterOfUser; // 处理delete方法
private final EntityDeletionOrUpdateAdapter __updateAdapterOfUser; // 处理update方法
在UserDao_Impl的类中除了数据库RoomDataBase实例外,还有三个成员变量分别为:__insertionAdapterOfUser、__deletionAdapterOfUser、__updateAdapterOfUser,从名字上可以看出来他们三个分别对应数据库增、删、改的三个操作,我们以insert操作为例,查看insert方法:
@Override
public void inertUser(User user) {
__db.beginTransaction();
try {
__insertionAdapterOfUser.insert(user);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
insert()方法的实现是在__insertionAdapterOfUser中执行的,查看__insertionAdapterOfUser的实现
this.__insertionAdapterOfUser = new EntityInsertionAdapter(__db) {
@Override
public String createQuery() { // 创建SupportSQLiteStatement时传入的Sql语句
return "INSERT OR ABORT INTO `user`(`id`,`strength`,`name`,`street`,`state`,`city`,`post_code`) VALUES (nullif(?, 0),?,?,?,?,?,?)";
}
@Override
public void bind(SupportSQLiteStatement stmt, User value) {
stmt.bindLong(1, value.getId());
stmt.bindLong(2, value.getStrength());
if (value.getName() == null) { // 判断此列是否为null,部位Null则设置数据
stmt.bindNull(3);
} else {
stmt.bindString(3, value.getName());
}
final Address _tmpAddress = value.getAddress();
if(_tmpAddress != null) {
if (_tmpAddress.getStreet() == null) {
stmt.bindNull(4);
} else {
stmt.bindString(4, _tmpAddress.getStreet());
}
if (_tmpAddress.getState() == null) {
stmt.bindNull(5);
} else {
stmt.bindString(5, _tmpAddress.getState());
}
if (_tmpAddress.getCity() == null) {
stmt.bindNull(6);
} else {
stmt.bindString(6, _tmpAddress.getCity());
}
stmt.bindLong(7, _tmpAddress.getPostCode());
} else {
stmt.bindNull(4);
stmt.bindNull(5);
stmt.bindNull(6);
stmt.bindNull(7);
}
}
};
__insertionAdapterOfUser的实例重写了两个方法:
insert()方法中创建SupportSQLiteStatement的实例,并调用bind()完成数据的绑定,然后执行stmt.executeInsert()插入数据
public final void insert(T entity) {
final SupportSQLiteStatement stmt = acquire(); // 最终创建的是FrameworkSQLiteStatement的包装的SQLiteStatement实例
try {
bind(stmt, entity); // 绑定要插入的数据
stmt.executeInsert(); // 提交保存数据,执行
} finally {
release(stmt);
}
}
@Override
public long executeInsert() { // 最终执行数据库的插入操作
return mDelegate.executeInsert();
}
在UserDao_Impl中自动实现了查询的方法selectUser:
@Override
public User[] selectUser(String name) {
final String _sql = "SELECT * FROM user WHERE name = ?";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1); // 创建RoomSQLiteQuery
int _argIndex = 1;
if (name == null) {
_statement.bindNull(_argIndex);
} else {
_statement.bindString(_argIndex, name);
}
final Cursor _cursor = __db.query(_statement); // 执行查询反会Cursor
try {
final int _cursorIndexOfId = _cursor.getColumnIndexOrThrow("id");
final int _cursorIndexOfStrength = _cursor.getColumnIndexOrThrow("strength");
final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
final int _cursorIndexOfStreet = _cursor.getColumnIndexOrThrow("street");
final int _cursorIndexOfState = _cursor.getColumnIndexOrThrow("state");
final int _cursorIndexOfCity = _cursor.getColumnIndexOrThrow("city");
final int _cursorIndexOfPostCode = _cursor.getColumnIndexOrThrow("post_code");
final User[] _result = new User[_cursor.getCount()];
int _index = 0;
while(_cursor.moveToNext()) {
final User _item;
final Address _tmpAddress;
if (! (_cursor.isNull(_cursorIndexOfStreet) && _cursor.isNull(_cursorIndexOfState) && _cursor.isNull(_cursorIndexOfCity) && _cursor.isNull(_cursorIndexOfPostCode))) {
_tmpAddress = new Address();
final String _tmpStreet;
_tmpStreet = _cursor.getString(_cursorIndexOfStreet);
_tmpAddress.setStreet(_tmpStreet);
final String _tmpState;
_tmpState = _cursor.getString(_cursorIndexOfState);
_tmpAddress.setState(_tmpState);
final String _tmpCity;
_tmpCity = _cursor.getString(_cursorIndexOfCity);
_tmpAddress.setCity(_tmpCity);
final int _tmpPostCode;
_tmpPostCode = _cursor.getInt(_cursorIndexOfPostCode);
_tmpAddress.setPostCode(_tmpPostCode);
} else {
_tmpAddress = null;
}
_item = new User();
final int _tmpId;
_tmpId = _cursor.getInt(_cursorIndexOfId);
_item.setId(_tmpId);
final int _tmpStrength;
_tmpStrength = _cursor.getInt(_cursorIndexOfStrength);
_item.setStrength(_tmpStrength);
final String _tmpName;
_tmpName = _cursor.getString(_cursorIndexOfName);
_item.setName(_tmpName);
_item.setAddress(_tmpAddress);
_result[_index] = _item;
_index ++;
}
return _result;
} finally {
_cursor.close();
_statement.release();
}
}
上面执行的也是数据库的正常操作,先创建了RoomSQLiteQuery的实例,在调用db。query()执行查询,查询返回Cursor实例,最终从Cursor中获取信息转换为对象并返回数据。
到此Room的使用和源码执行流程就到此结束了,本文旨在执行的流程分析,具体的如何使用SQLite数据库操作的读者可以自己点击源码查看,不过使用的SQLite的查询和添加方法和平时使用的不同,读者想分析的话就会找到了,好了,希望本篇文章对想了解和使用Room组件的同学有所帮助!
本文使用Room的Demo,欢迎Star