数据库工具:Room
Room结构
导入依赖
app的build.gradle中开启kapt:
apply plugin: 'kotlin-kapt'
并导入以下依赖:
def room_version = '2.2.4'
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
// Test helpers
testImplementation "androidx.room:room-testing:$room_version"
kapt 'android.arch.persistence.room:compiler:1.1.1'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
//注意:对于基于 Kotlin 的应用,请确保使用 kapt 而不是 annotationProcessor。您还应添加 kotlin-kapt 插件。
基础三大件:Entity,Dao,Database
Entity:数据库的结构
语法
使用@Entity注解Entity类
使用@PrimaryKey(autoGenerate = true),@ColumuInfo(name = "")注解键
示例
@Entity(tableName = "word_table") //数据库结构
data class Word (
@PrimaryKey(autoGenerate = true)
var id: Int,
@ColumnInfo(name = "english")
var word: String,
@ColumnInfo(name = "chinese")
var chineseMeaning: String
)
Dao:数据库的操作
语法
使用 @Dao注解接口
使用 @Insert,@Update,@Delete,@Query("DELETE FROM WORD"),@Query("SELECT * FROM WORD ORDER BY ID DESC") 等注解数据库操作
示例
@Dao //数据库操作
interface WordDao {
@Insert
fun insertWords(vararg words: Word)
@Update
fun updateWords(vararg words: Word)
@Delete
fun daleteWords(vararg words: Word)
@Query("DELETE FROM WORD")
fun deleteAllWords()
@Query("SELECT * FROM WORD ORDER BY ID DESC")
fun getAllWords() : LiveData<List<Word>> //使用LiveData,观测数据改变并自动
}
Database:数据库工具类
语法
使用 @Database(entities = [com.example.roomtest.Word::class], version = 1, exportSchema = false) 注解类
尽量使用抽象类并且使用单例模式
示例
@Database(entities = [com.example.roomtest.Word::class], version = 1, exportSchema = false)
//获取数据库实体
abstract class WordDatabase : RoomDatabase() {
abstract fun getWordDao() : WordDao
/**
* 单例数据库
*/
companion object {
private var instance: WordDatabase? = null
@Synchronized
fun get(context: Context): WordDatabase {
if (instance == null) {
instance = Room.databaseBuilder(context.applicationContext,
WordDatabase::class.java, "word_database")
.build()
}
return instance!!
}
}
}
进阶
一、使用ViewModel
1,导入ViewModel模板
2,示例
class WordViewModel(application: Application) : AndroidViewModel(application) {
var wordDao: WordDao
var allWordLive: LiveData<List<Word>>
init {
val wordDatabase = WordDatabase.get(application)
wordDao = wordDatabase.getWordDao()
allWordLive = wordDao!!.getAllWords()
}
fun insertWords(vararg words: Word) {
InsertAsyncTask(wordDao!!).execute(*words)
}
fun clearWords() {
ClearAsyncTask(wordDao!!).execute()
}
inner class InsertAsyncTask(val wordDao: WordDao) : BaseAsyncTask(wordDao) {
override fun doInBackground(vararg params: Word): Void? {
wordDao.insertWords(*params)
return null
}
}
inner class UpdateAsyncTask(val wordDao: WordDao) : BaseAsyncTask(wordDao) {
override fun doInBackground(vararg params: Word): Void? {
return null
}
}
inner class DeleteAsyncTask(val wordDao: WordDao) : BaseAsyncTask(wordDao) {
override fun doInBackground(vararg params: Word): Void? {
return null
}
}
inner class ClearAsyncTask(val wordDao: WordDao) : BaseAsyncTask(wordDao) {
override fun doInBackground(vararg params: Word): Void? {
wordDao.deleteAllWords()
return null
}
}
}
以上ViewModel将数据的操作与使用放在一起,还可以继续分层:将数据的使用剥离出去
二、使用仓库Reposity访问数据
示例
/**
* 数据访问
*/
class WordRepository(val context: Context) {
private var allWordsLive : LiveData<List<Word>>
private var wordDao : WordDao
init {
val wordDatabase = WordDatabase.get(context)
wordDao = wordDatabase.getWordDao()
allWordsLive = wordDao.getAllWords()
}
fun insertWords(vararg words: Word) {
InsertAsyncTask(wordDao!!).execute(*words)
}
fun clearWords() {
ClearAsyncTask(wordDao!!).execute()
}
inner class InsertAsyncTask(val wordDao: WordDao) : BaseAsyncTask(wordDao) {
override fun doInBackground(vararg params: Word): Void? {
wordDao.insertWords(*params)
return null
}
}
inner class ClearAsyncTask(val wordDao: WordDao) : BaseAsyncTask(wordDao) {
override fun doInBackground(vararg params: Word): Void? {
wordDao.deleteAllWords()
return null
}
}
}
改造后的ViewModel:
class WordViewModel(application: Application) : AndroidViewModel(application) {
private val wordDao: WordDao
var allWordLive: LiveData<List<Word>>
init {
val wordDatabase = WordDatabase.get(application)
wordDao = wordDatabase.getWordDao()
allWordLive = wordDao!!.getAllWords()
}
fun insertWords(vararg words: Word) {
WordRepository(getApplication()).insertWords(*words)
}
fun clearWords() {
WordRepository(getApplication()).clearWords()
}
}
三、升级数据库
Room.databaseBuilder(context.applicationContext,
WordDatabase::class.java,
"word_database")
.fallbackToDestructiveMigration() //破坏式升级:升级版本后清空原有内容
.addMigrations(MIGRATION_1_2) //无痛改变
.build()
val MIGRATION_1_2 : Migration = object : Migration(1, 2) { //类的参数分别为新旧数据库的版本号
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("") //使用SQL语句进行数据库操作
}
}
项目的其他代码
基累BaseAsyncTask:
open class BaseAsyncTask(private val wordDao: WordDao) : AsyncTask<Word, Void, Void>() {
override fun doInBackground(vararg words: Word): Void? {
return null
}
}
Acitvity:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import java.lang.StringBuilder
class MainActivity : AppCompatActivity() {
private lateinit var insert: Button
private lateinit var update: Button
private lateinit var delete: Button
private lateinit var clear: Button
private lateinit var content : TextView
private lateinit var wordViewModel: WordViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
insert = findViewById(R.id.insert)
update = findViewById(R.id.update)
delete = findViewById(R.id.delete)
clear = findViewById(R.id.clear)
content = findViewById(R.id.content)
wordViewModel = ViewModelProviders.of(this)[WordViewModel::class.java]
wordViewModel.allWordLive.observe(this, Observer {
var text = StringBuilder()
for (x in it) {
text.append(x.id).append(":").append(x.word).append("=").append(x.chineseMeaning).append("
")
}
content.text = text.toString()
})
insert.setOnClickListener {
var word1 = Word(0, "Hello", "你好")
var word2 = Word(0, "World", "世界")
wordViewModel.insertWords(word1, word2)
}
clear.setOnClickListener {
wordViewModel.clearWords()
}
}
}
xml布局:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.6" />
<ScrollView
android:id="@+id/scrollView2"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0">
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="24sp" />
</ScrollView>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.8" />
<Button
android:id="@+id/insert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="insert"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline"
app:layout_constraintVertical_bias="0.52" />
<Button
android:id="@+id/update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="update"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/guideline"
app:layout_constraintVertical_bias="0.52" />
<Button
android:id="@+id/clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="clear"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline3" />
<Button
android:id="@+id/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="delete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/guideline3" />
</androidx.constraintlayout.widget.ConstraintLayout>
懒加载控件:Paging
声明依赖
def paging_version = "2.1.1"
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
Paging + Room + RecyclerView
数据类型
Dao中,数据使用DataSource.Factory<Key, Value>格式
@Dao
interface StudentDao {
@Query("SELECT * FROM student_table ORDER BY id")
fun getAllStudents() : DataSource.Factory<Int, Student>
}
RecycleView的适配器
改用PagedListAdapter<数据类型, Holder>:
class MyPagedAdapter : PagedListAdapter<Student, MyViewHolder>(DIFF_CALLBACK) {
companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Student>() {
override fun areItemsTheSame(oldItem: Student, newItem: Student): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Student, newItem: Student):Boolean {
return oldItem.studentNumber == newItem.studentNumber
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
}
class MyViewHolder(itemView: View) : ViewHolder(itemView) {
}
}
装配数据
private lateinit var studentDao: StudentDao //Dao类
private lateinit var studentDatabase : StudentDatabase //数据库类
private lateinit var pagedAdapter: MyPagedAdapter //适配器类
private lateinit var allStudentsLivePaged : LiveData<PagedList<Student>> //分页数据管理
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
pagedAdapter = MyPagedAdapter()
list.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
list.adapter = pagedAdapter
studentDatabase = StudentDatabase.getInstance(this)
studentDao = studentDatabase.getStudentDao()
//第二个参数为一次加载数据的个数
allStudentsLivePaged = LivePagedListBuilder(studentDao.getAllStudents(), 2).build()
}