Android知识总结
一、导包
在Java中使用
//Room
implementation 'androidx.room:room-runtime:2.3.0-alpha03'
//编译使用到,通过apt生成相应文件
annotationProcessor 'androidx.room:room-compiler:2.3.0-alpha03'
在Kotlin中使用
apply plugin: 'kotlin-kapt'
...
dependencies {
//协程
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
//Room
implementation 'androidx.room:room-runtime:2.3.0-alpha03'
kapt 'androidx.room:room-compiler:2.3.0-alpha03'
implementation 'androidx.room:room-ktx:2.3.0-alpha03'
}
在分包中使用都学要导androidx.room:room-compiler
二、讲解
Android推荐的架构组件
Entity:当使用架构组件时,Entity是描述数据库表的类,这个类通常使用注解。
SQLite database:SQLite是一个数据库,存储数据。为了简单起见,忽略其他的存储工具(如web服务器等)。Room持久性库用于创建和维护数据库。
DAO:即数据访问对象。之前使用 SQLiteOpenHelper
类定义这些内容。当使用DAO时,我们可以调用方法,room做其余的操作。
Room database:SQLite数据库之上的数据库层,负责处理以前使用SQLiteOpenHelper处理的普通任务。Room数据库使用DAO查询SQLite数据库。
Repository:Repository是用于管理多个数据资源,例如数据库,网络等。
ViewModel:为UI提供数据。ViewModel作为Repository和UI的通信中心。ViewModel在数据配置更改后仍然存在。
LiveData:LiveData是可以被观察到的数据持有类。它里面缓存或持有了最新的数据。当数据改变时会通知它的观察者。LiveData是可以感知生命周期的。UI组件只是观察相关数据,不会停止或恢复观察。 LiveData自动管理所有这些,因为它在观察时意识到相关的生命周期状态变化。
三、使用
1)、Entity 数据类
@Entity(tableName = "user")
data class User(
@ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
var name: String,
@ColumnInfo(name = "sex", typeAffinity = ColumnInfo.INTEGER)
var sex: Int = 0
) {
//主键,是否自增
@NonNull
@PrimaryKey(autoGenerate = true)
//别名,数据类型
@ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
var id: Int = 0
companion object {
val sexText = arrayListOf("男", "女", "保密")
}
fun getSexText() = sexText[sex]
override fun toString(): String {
return "User(id=$id, name=$name, sex=${getSexText()})"
}
}
2)、Dao接口类
@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getAll(): LiveData>
@Query("SELECT * FROM user")
fun getAllUser(): PagingSource
@Query("SELECT * FROM user WHERE id = :id")
fun findUserById(id: Long): User
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg users: User)
@Update
fun update(vararg users: User): Int
@Delete
fun del(vararg user: User): Int
@Query("DELETE FROM user")
fun delAll()
}
3)、抽象database类
@Database(entities = [User::class], version = 1,, exportSchema = true)
abstract class UserDb : RoomDatabase() {
abstract fun getUserDao(): UserDao?
companion object {
@Volatile
private var INSTANCE: UserDb? = null
fun getDataBase(context: Context): UserDb {
if (INSTANCE == null){
synchronized(UserDb::class.java){
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context.applicationContext,
UserDb::class.java, "user_db")
//发生错误时直接删除旧版数据,重新创建
.fallbackToDestructiveMigration()
//.allowMainThreadQueries() //可以在主线程操作
//.addMigrations(MIGRATION_1_2) //升级数据库
.build()
}
}
}
return INSTANCE!!
}
//升价数据库
private val MIGRATION_1_2 = object :Migration(1, 2){
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE user ADD COLUMN age INTEGER" +
"NOT NULL DEFAULT 0")
}
}
}
}
Room是置于SQLite数据库上面的数据库。之前我们使用 SQLiteOpenHelper
类处理一些与数据相关的任务,而Room处理之前使用 SQLiteOpenHelper
类处理普通的任务。
- Room使用DAO查询数据库
- 默认情况下,为了提高UI性能,Room不允许在主线程中执行查询数据库操作。LiveData在后台线程上异步更新数据。
- Room 提供SQLite语句编译时检查
- 自定义的Room类必须是抽象类且必须继承
RoomDatabase
- 通常,在整个APP中,只需要一个Room database实例。
4)UserRepository
class UserRepository {
var userDao: UserDao? = null
var all: LiveData>? = null
constructor(app: Application) {
val dataBase = UserDb.getDataBase(app);
userDao = dataBase.getUserDao();
all = userDao!!.getAll()
}
fun insert(user: User) {
Observable.create {
userDao!!.insert(user)
}.subscribeOn(Schedulers.io())
.subscribe { }
}
fun delete(){
Thread(Runnable {
userDao!!.delAll()
}).start()
}
fun update(user: User) {
val launch = GlobalScope.launch(Dispatchers.IO) {
userDao!!.update(user)
}
}
}
Repository管理数据的查询线程,同时,可以使用多个后端。在常规情况下,Repository主要实现从服务端拉取数据还是从本地数据库拉取数据的逻辑。
5)、ViewModel
class MainViewModel : AndroidViewModel, LifecycleObserver {
var data: LiveData>? = null
var userRepository: UserRepository
constructor(app: Application) : super(app) {
userRepository = UserRepository(app)
data = userRepository.all
}
fun add(user : User){
userRepository.insert(user)
}
fun delete(){
userRepository.delete()
}
fun update(user: User) {
userRepository.update(user)
}
}
ViewModel将UI数据和Activity和Fragment类进行分离,更符合单一职责原则:Activity和Fragment负责展示UI,而ViewModel负责持有并处理UI所需要的所有数据。
在ViewModel 中,使用LiveData更新UI的数据。因为LiveData有以下几个优点:
- 可以监听数据,只有当数据更改时才会更新UI。
- 通过ViewModel可以将Repository和UI完全隔离。在ViewModel中不会直接进行数据库调用,这使得代码更方便进行测试。
6)、操作数据库
操作数据库一般要做子线程中进行
fun update(view: View) {
val user = users!![1]
user.name = "Hua"
user.sex = 55
viewModel!!.update(user)
}
fun delete(view: View) {
viewModel!!.delete()
}
fun add(view: View) {
viewModel!!.add(User("XX", 33, 11))
}
fun check(view: View) {
viewModel!!.data!!.observe(this,
Observer> {
baseAdapter!!.addData(it as MutableList)
})
}
四、使用Schema
build.gradle中配置
defaultConfig {
...
//指定room.schemaLocation生成的文件路径
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
}
给RoomDatabase设置exportSchema注解为true
@Database(entities = [User::class], version = 1, exportSchema = true)
abstract class UserDb : RoomDatabase() {
}
会生成一个json文件
五、销毁和重建策略
例如:把user
表的字段sex,从INTEGER
类型变为TEXT
类型。
步骤
1、创建一张新的符合要求的临时表temp_user
2、将数据从旧的表user复制到临时表temp_user中
3、删除旧表user
4、将临时表temp_user重命名为user
private val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
//创建临时表
database.execSQL(
"CREATE TABLE IF NOT EXISTS temp_user (" +
" id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
"name TEXT NOT NULL," +
"sex TEXT NOT NULL DEFAULT 'M')")
//拷贝数据
database.execSQL("INSERT INTO temp_user(id, name, sex) SELECT id, name, sex FROM user")
//删除user表
database.execSQL("DROP TABLE user")
//将临时表temp_user重命名为user
database.execSQL("ALTER TABLE temp_user RENAME TO user")
}
}