AndroidJetpack数据处理之数据库Room和懒加载Paging

数据库工具:Room

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("\n")
            }
            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()
}

猜你喜欢

转载自www.cnblogs.com/lizhenxin/p/12433707.html