上篇文章 Android Jetpack 库架构组件 ViewModel+LiveData 基础使用 的
示例2
中,使用ViewModel+ LiveData
的方式实现了数据库数据查询并分页
显示的效果,而这里的数据库用的就是Room
,分页使用的Paging
。
Room
持久性库在SQLite
的基础上提供了一个抽象层,让用户能够在充分利用SQLite
的强大功能的同时,获享更强健的数据库访问机制。
也就是 Room
是在SQlite
的基础上封装了接口,使得SQlite
更加易用。
使用Room
需要包含 3
个主要组件:
Room.databaseBuilder()
或 Room.inMemoryDatabaseBuilder()
获取 Database
的实例。Entity
:表示数据库中的表。DAO
:包含用于访问数据库的方法。Room
不同组件之间的关系如下图所示:
步骤1:应用使用
Room
数据库来获取与该数据库关联的数据访问对象 (DAO
)。:
步骤2:应用使用每个DAO
从数据库中获取实体,然后再将对这些实体的所有更改保存回数据库中。
步骤3:应用使用实体来获取和设置与数据库中的表列相对应的值。
示例:
(1)获取数据库中学生的姓名列表
Module -> build.gradle
的引入版本依赖查看:https://developer.android.google.cn/jetpack/androidx/releases/room#declaring_dependencies
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
// 还有一些可选项,可查看上面的版本依赖链接
@Entity
注解@Entity(tableName = "Student")
data class Student(
// 字段1,主键自增
@PrimaryKey(autoGenerate = true)
val id: Int,
// 字段2
val name: String)
DAO
接口,用@Dao
注解实现数据库的增删改查@Dao
interface StudentDao {
// DataSource.Factory 获取 Room 数据库中所有学生名称按升序返回
@Query("SELECT * FROM Student ORDER BY name COLLATE NOCASE ASC")
fun getAllStudent(): DataSource.Factory
// 插入学生集合
@Insert
fun insert(students: List)
// 插入一个学生
@Insert
fun insert(student: Student)
}
AppDatabase
扩展 RoomDatabase
的抽象类,并创建数据库实例。// 把实体类添加到数组中,定义数据库版本号
@Database(entities = [Student::class], version = 1)
abstract class StudentDb:RoomDatabase() {
// 定义 DAO
abstract fun studentDao(): StudentDao
// 静态方法创建实例和往数据库插入学生姓名信息
companion object {
private var instance: StudentDb? = null
@Synchronized
fun get(): StudentDb {
if (instance == null) {
instance = Room.databaseBuilder(applicationContext,
StudentDb::class.java, "StudentDatabase").build()
}
return instance!!
}
// 默认数据
fun initData(){
ioThread {
// 单线程池
get().studentDao().insert(
CHEESE_DATA.map {
Student(
id = 0,
name = it
)
})
}
}
}
}
// 学生姓名
private val CHEESE_DATA = arrayListOf(
"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
"Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag",
"Airedale", "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", // 15
"American Cheese", "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro",
"Appenzell", "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String",
"Aromes au Gene de Marc", "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", // 30
"Avaxtskyr", "Baby Swiss", "Babybel", "Baguette Laonnaise", "Bakers",
"Baladi", "Balaton", "Bandal", "Banon", "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
"Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
"Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
"Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
"Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
"Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)"
)
class HomeViewModel(context: Context): BaseViewModel() {
companion object {
private const val PAGE_SIZE = 15
private const val ENABLE_PLACEHOLDERS = false
}
// val mContext = context
// val dao = StudentDb.get(mContext).studentDao()
// 获取 DAO,通过 DAO 去获取数据库数据
val dao = StudentDb.get().studentDao()
// dao.getAllStudent() 这里即返回了所有的学生姓名
// 这里使用 Paging 实现分页,文章下面再说。
val allStudents = LivePagedListBuilder(dao.getAllStudent(), PagedList.Config.Builder()
.setPageSize(PAGE_SIZE) //配置分页加载的数量
.setEnablePlaceholders(ENABLE_PLACEHOLDERS) //配置是否启动PlaceHolders
.setInitialLoadSizeHint(PAGE_SIZE) //初始化加载的数量
.build()).build()
}
Tip:
别忘了在Application
调用数据的初始化,不然查询数据库时查不到学生数据。(或者你编写学生姓名一条条插入数据库)
// 初始化数据库数据
StudentDb.initData()
分页库可帮助您一次加载和显示一小块数据。按需载入部分数据会减少网络带宽和系统资源的使用量。
分页库的关键组件是 PagedList
类,用于加载应用数据块或页面。随着所需数据的增多,系统会将其分页到现有的 PagedList
对象中。
如果任何已加载的数据发生更改,会从LiveData
或基于 RxJava2
的对象向可观察数据存储器发出一个新的 PagedList 实例
。
随着 PagedList
对象的生成,应用界面会呈现其内容,同时还会考虑界面控件的生命周期。
分页库支持以下数据架构:
Retrofit
使用。Room
使用。Retrofit + Room
使用。下面以查询数据库显示在RecyclerView
为例介绍使用步骤。
示例:
(1)将数据库的数据查询出来显示在RecyclerView
Module -> build.gradle
的引入版本依赖查看:https://developer.android.google.cn/jetpack/androidx/releases/paging#declaring_dependencies
dependencies {
def paging_version = "2.1.2"
implementation "androidx.paging:paging-runtime:$paging_version" // For Kotlin use paging-runtime-ktx
// alternatively - without Android dependencies for testing
testImplementation "androidx.paging:paging-common:$paging_version" // For Kotlin use paging-common-ktx
// optional - RxJava support
implementation "androidx.paging:paging-rxjava2:$paging_version" // For Kotlin use paging-rxjava2-ktx
}
Room
数据库提供了 DataSource.Factory
对象或者 自定义对象@Dao
interface StudentDao {
@Query("SELECT * FROM Student ORDER BY name COLLATE NOCASE ASC")
fun getAllStudent(): DataSource.Factory
}
PagedList
。将 DataSource.Factory
的实例传递到 LivePagedListBuilder
或 RxPagedListBuilder
对象。class HomeViewModel(context: Context): BaseViewModel() {
companion object {
private const val PAGE_SIZE = 15
private const val ENABLE_PLACEHOLDERS = false
}
val dao = StudentDb.get().studentDao()
// 传递实例给 LivePagedListBuilder ,分页配置
val allStudents = LivePagedListBuilder(dao.getAllStudent(), PagedList.Config.Builder()
.setPageSize(PAGE_SIZE) //配置分页加载的数量
.setEnablePlaceholders(ENABLE_PLACEHOLDERS) //配置是否启动PlaceHolders
.setInitialLoadSizeHint(PAGE_SIZE) //初始化加载的数量
.build()).build()
}
PagedListAdapter
需要接收一个DiffUtil.ItemCallback
参数进行对象的构建。
class StudentAdapter: PagedListAdapter(diffCallback) {
override fun onBindViewHolder(holder: StudentViewHolder, position: Int) {
holder.bindTo(getItem(position))
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StudentViewHolder =
StudentViewHolder(parent)
companion object {
// 用于计算列表中两个非空 item 之间的差异的回调。
private val diffCallback = object : DiffUtil.ItemCallback() {
// 检查两个对象是否表示同一 item 数据。
override fun areItemsTheSame(oldItem: Student, newItem: Student): Boolean =
oldItem.id == newItem.id
// 检查两个项目是否具有相同的数据。
override fun areContentsTheSame(oldItem: Student, newItem: Student): Boolean =
oldItem == newItem
}
}
}
UI
页面使用数据override fun initView() {
val adapter = StudentAdapter()
val layoutManager = LinearLayoutManager(activity)
rv_list.layoutManager = layoutManager
rv_list.adapter = adapter
// 将数据的变化反映到UI上
viewModel.allStudents.observe(this, Observer {
adapter.submitList(it)
})
}
详细使用代码请参见:YGragon/FrameDemo
Paging
是分页库,也就是将大量的数据通过一段一段的返回给页面展示。而大量的数据可以从网络
请求返回,也可以是从Room
数据库中读取。从Room
数据库中读取需要创建数据库实例
、DAO
、Entity
,而将数据展示在列表需要用到PagedListAdapter
。PagedListAdapter
继承自RecyclerView.Adapter
,在该Adapter
中需要传入diffCallback
,用于判断数据是否是最新的。最后就是在UI
中通过LiveData
监听数据的变化及时更新到 UI
。
佛系原创号主