Android jetpack的Paging和Room使用

介紹

Paging主要是用來結合RecyclerView進行使用,是一種分頁加載解決方案,這樣Paging每次只會加載總數據的一部分。 Room是Google提供的一個ORM庫。 本文的代碼來自官方例子:官方示例地址html

使用Paging Room

  1. 添加依賴
def room_version = "2.2.0-alpha02"
    implementation "androidx.room:room-runtime:$room_version"
    implementation "androidx.room:room-rxjava2:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
    kapt "androidx.room:room-compiler:$room_version"

    def paging = "2.1.0"
    implementation "androidx.paging:paging-runtime-ktx:$paging"


複製代碼
  1. 數據庫的建立 示例經過 Room數據庫獲取數據源,用來在Recyclerview展現咱們的數據,可是正常的開發主要以網絡請求方式獲取來獲取數據源(網絡請求和Paging的GitHub代碼)。

簡單介紹一下Room。Room提供了三個主要的組件:java

@Database:@Database用來註解類,而且註解的類必須是繼承自RoomDatabase的抽象類。該類主要做用是建立數據庫和建立Dao。而且會生成XXX(類名)_Impl的實現類android

@Entity:@Entity用來註解實體類,@Database經過entities屬性引用被@Entity註解的類,並利用該類的全部字段做爲表的列名來建立表。使用@Database註解的類中必須定一個不帶參數的方法,這個方法返回使用@Dao註解的類git

@Dao:@Dao用來註解一個接口或者抽象方法,該類的做用是提供訪問數據庫的方法。而且會生成XXX(類名)_impl的實現類, (1)建立Student實體類: 主要是定義了自增的主鍵github

@Entity
data class Student(@PrimaryKey(autoGenerate = true) val id: Int,val name: String)
複製代碼

(2)建立Dao: 定義了一些數據庫的操做方法。其中DataSource表示數據源的意識,數據源的改變會驅動UI的更新。數據庫

@Dao
interface StudentDao {

    @Query("Select * from Student ORDER BY name COLLATE NOCASE ASC ")
    fun queryByName(): DataSource.Factory<Int,Student>

    @Insert`在這裏插入代碼片`
    fun insert(students: List<Student>)

    @Insert
    fun insert(student: Student)

    @Delete
    fun delete(student: Student)
}
複製代碼

(3)建立數據庫:bash

@Database(entities = arrayOf(Student::class) ,version = 1)
abstract class StudentDb : RoomDatabase(){
    abstract fun studentDao(): StudentDao

    companion object{
        private var instance: StudentDb? = null

        @Synchronized
        fun get(context: Context): StudentDb{
            if(instance == null){
                instance = Room.databaseBuilder(context.applicationContext,
                    StudentDb::class.java,"StudentDataBase")
                    .addCallback(object : Callback() {
                        override fun onCreate(db: SupportSQLiteDatabase) {
                            fillInDb(context.applicationContext)
                        }
                    }).build()
            }
            return instance!!
        }

        private fun fillInDb(context: Context){
            ioThread{
                get(context).studentDao().insert(STUDENT_DATA.map {
                    Student(id = 0,name = it)
                })
            }
        }
    }
}

private val STUDENT_DATA = arrayListOf(.........);

複製代碼
  1. UI顯示 (1)建立StudentViewHolder
class StudentViewHolder (parent: ViewGroup) : RecyclerView.ViewHolder(
    LayoutInflater.from(parent.context).inflate(R.layout.adapter_paging,parent,false)){
    private val nameView = itemView.findViewById<TextView>(R.id.name)

    var student : Student? = null

    fun bindTo(student: Student?){
        this.student = student
        nameView.text = student?.name
    }
}
複製代碼

(2)建立PagedListAdapter的實現類。 其中的DiffUtil.ItemCallback<> 實例,當數據源發生變化時,會回調DiffUtil.ItemCallback中兩個抽象方法,確認數據和以前是否發生了改變,若是改變則調用Adapter更新UI。 areItemTheSame方法:是否爲同一個Item areContentsTheSame方法:數據內容是否發生變化網絡

class StudentAdapter : PagedListAdapter<Student,StudentViewHolder>(diffCallback){
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StudentViewHolder {
        return StudentViewHolder(parent)
    }

    override fun onBindViewHolder(holder: StudentViewHolder, position: Int) {
        holder.bindTo(getItem(position))
    }


    companion object{
        private val diffCallback = object : DiffUtil.ItemCallback<Student>(){
            override fun areContentsTheSame(oldItem: Student, newItem: Student): Boolean {
                Log.e("tag","areContentsTheSame"+Thread.currentThread().name+ oldItem.name +" new :"+newItem.name)
                return oldItem.id == newItem.id
            }

            override fun areItemsTheSame(oldItem: Student, newItem: Student): Boolean {
                Log.e("tag","areItemsTheSame"+ oldItem.name +" new :"+newItem.name)
                return oldItem == newItem
            }
        }
    }
}
複製代碼

(3)建立ViewModel toLiveData方法內部經過LivePagedListBuilder來構建PagedList。返回的是LiveData<PagedList>,用於UI層監聽數據源變化。app

Config是用於對PagedList進行構建配置的類。 pageSize用於指定每頁數據量。 enablePlaceholders表示是否將未加載的數據以null存儲在在PageList中。ide

class StudentViewModel (app: Application) : AndroidViewModel(app){
    val dao = StudentDb.get(app).studentDao()

    val allStudents = dao.queryByName().toLiveData(Config(pageSize = 30,enablePlaceholders = true,maxSize = Int.MAX_VALUE))

    fun insert(name: String) = ioThread{
        dao.insert(Student(id = 0,name = name))
    }

    fun remove(student: Student) = ioThread{
        dao.delete(student)
    }
}
複製代碼

(4)Activity中使用 在Activity中給ViewModel中LiveData<PagedList>進行添加一個觀察者,每當觀察到數據源中數據的變化,就能夠調用StudentAdapter的submitList方法把最新的數據交給Adapter去展現了。

class PagingActivity : AppCompatActivity(){

    private lateinit var viewModel: StudentViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_paging)
        viewModel = ViewModelProviders.of(this).get(StudentViewModel::class.java)
        val adapter = StudentAdapter()
        studenrecy.adapter = adapter
        viewModel.allStudents.observe(this, Observer(adapter::submitList))

        initAddButtonListener()

        initSwipeToDelete()
    }

    private fun initSwipeToDelete(){
        ItemTouchHelper(object : ItemTouchHelper.Callback(){
            override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
                return makeMovementFlags(0,ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT)
            }

            override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder
            ): Boolean {
                return false
            }

            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                (viewHolder as StudentViewHolder).student?.let { viewModel.remove(it) }
            }
        }).attachToRecyclerView(studenrecy)
    }

    private fun addCheese() {
        val newCheese = inputText.text.trim()
        if (newCheese.isNotEmpty()) {
            viewModel.insert(newCheese.toString())
            inputText.setText("")
        }
    }
    private fun initAddButtonListener(){
        addButton.setOnClickListener {
            addCheese()
        }
        // when the user taps the "Done" button in the on screen keyboard, save the item.
        inputText.setOnEditorActionListener { _, actionId, _ ->
            if (actionId == EditorInfo.IME_ACTION_DONE) {
                addCheese()
                return@setOnEditorActionListener true
            }
            false // action that isn't DONE occurred - ignore } // When the user clicks on the button, or presses enter, save the item. inputText.setOnKeyListener { _, keyCode, event -> if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) { addCheese() return@setOnKeyListener true } false // event that isn't DOWN or ENTER occurred - ignore
        }
    }
}
複製代碼

小結

總結一下大概的流程: (1)當一條新的數據插入到數據庫即數據源發生變化時,會回調到DataSource.InvalidatedCallback,在ComputableLiveData的compute方法中DataSource會被初始化(dataSourceFactory.create())。 (2)LiveData後臺線程就會建立一個新的PagedList。新的PagedList會被mLiveData.postValue(value)發送到UI線程的PagedListAdapter中。 (3)PagedListAdapter使用DiffUtil在對比如今的Item和新建Item的差別。當對比結束,PagedListAdapter經過調用RecycleView.Adapter.notifyItemInserted()將新的item插入到適當的位置。

具體流程以下圖所示:

在這裏插入圖片描述
主要的幾個類: (1)DataSource: 數據源,數據的改變會驅動列表的更新。它有三個主要的子類。

PositionalDataSource: 主要用於加載數據可數有限的數據。好比加載本地數據庫,對應WrapperPositionalDataSource分裝類。還有個子類LimitOffsetDataSource,其中數據庫返回的就是該子類。

ItemKeyedDataSource:主要用於加載逐漸增長的數據。好比說網絡請求的數據隨着不斷的請求獲得的數據愈來愈多,對應WrapperItemKeyedDataSource封裝類。

PageKeyedDataSource:這個和ItemKeyedDataSource有些類似,都是針對那種不斷增長的數據。這裏網絡請求獲得數據是分頁的,對應WrapperPageKeyedDataSource封裝類。

(2)PageList: 核心類,它從數據源取出數據,同時,它負責控制第一次默認加載多少數據,以後每一次加載多少數據等等,並將數據的變動反映到UI上。 (4)DataSource.Factory這個接口的實現類主要是用來獲取的DataSource數據源。 (5)PagedListAdapter繼承自RecyclerView.Adapter,RecyclerView的適配器,經過DiffUtil分析數據是否發生了改變,負責處理UI展現的邏輯。 (6)LivePagedListBuilder經過這個類來生成對應的PagedList,內部主要有ComputableLiveData類。 主要的流程和類以下所示:

官網說明地址

在這裏插入圖片描述
相關文章
相關標籤/搜索