iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
0
Mobile Development

Android Architecture Components 學習心得筆記系列 第 21

Day 21 Paging (三) (NetWork + Database) Datasource

  • 分享至 

  • xImage
  •  

Paging (三) (NetWork + Database) Datasource

先說,今天的是一個失敗的案例。
因為一直找不出原因,一直在猶豫要不要放上來的時候,看到某位邦友的文章裡面提到心得筆記的話題,裡面有一篇文章大大的啟發了我 每一篇心得都有價值——為什麼初學者才更應該要寫心得筆記

裡面提到,在一開始學習新東西時一定都會有撞牆、卡關的地方,這時候反而更應該把這些點滴記錄下來,因為之後學會了、變強了,再也回不去當時那種矇矇懂懂的感覺,這時候如果有人也遇到同樣的問題,已經無法站在他們的角度去思考問題了。

所以決定把這次失敗的經驗記錄下來,也許未來有一天找出問題了再回來補上正確的,順便回憶一下初學時的撞牆期,也是一個不錯的經驗。

進入正題

本次目標

把 DataSource 換成 (NetWork + Database),每次都會先去要 Database 的資料,如果 Database 沒東西再去呼叫 API 抓,一從網路上抓到資料回來後立刻存進 Database 裡面,這樣下次再開啟 APP 就會有 Database 的資料可以用了。

PagedList.BoundaryCallback

這是 Paging 在給 Item 設定數據時的回調,主要實作兩個方法

onZeroItemsLoaded 沒有 Item 有數據時呼叫這個方法,通常是第一次進入列表時調用。
onItemAtEndLoaded 在讀到最後一個有數據的 Item 時呼叫,這時候會去跟 DataSource 要新的資料塞進接下來的 Item 裡。

這裡我參考 Google Sample Code 的寫法把呼叫 API 和存進 Room 的方法拉出來,整個 class 會長這樣

class PagingBoundaryCallback(context: Context) :
    PagedList.BoundaryCallback<DataItem>() {

    private var page = 1

    private val api = AllPlayerApi.api

    private val dao = DataItemDbHelper(context).getRoomDataItemDao()

    override fun onZeroItemsLoaded() {
        super.onZeroItemsLoaded()

        api.getAllPlayer().enqueue(createWebserviceCallback())
    }

    override fun onItemAtEndLoaded(itemAtEnd: DataItem) {
        super.onItemAtEndLoaded(itemAtEnd)

        api.getAllPlayer(page).clone().enqueue(createWebserviceCallback())
    }

    private fun createWebserviceCallback(): Callback<AllPlayerData> {

        return object : Callback<AllPlayerData> {
            override fun onFailure(call: Call<AllPlayerData>?, t: Throwable?) { }

            override fun onResponse(call: Call<AllPlayerData>?, response: Response<AllPlayerData>) {

                insertItemsIntoDb(response)
                page++
            }
        }
    }

    private fun insertItemsIntoDb(response: Response<AllPlayerData>) {

        GlobalScope.launch {

            response.body()!!.data!!.forEach {

                dao.insert(it)
            }
        }
    }
}

延續上一篇,因為 DataSource 的來源變成本地端的 Database,所以要修改一下 Repository,讓他從 Remote 拿資料變成和 Local Database 拿資料

class PagingRepository {

    private lateinit var localDataSource: DataSource.Factory<Int, DataItem>

}

新增的這個 localDataSource 代表是從本地端資料庫的數據來源。

fun getDataItem(application: Application): LiveData<PagedList<DataItem>> {

        val pagedListLiveData: LiveData<PagedList<DataItem>> by lazy {

            localDataSource = DataItemDbHelper(application).getRoomDataItemDao().getAllDataItem()

            val config = PagedList.Config.Builder()
                .setPageSize(25)
                .setEnablePlaceholders(false)
                .build()

            LivePagedListBuilder(localDataSource, config)
                .setBoundaryCallback(PagingBoundaryCallback(application))
                .build()
        }

        return pagedListLiveData
    }

getDataItem 方法回傳 LiveData,裡面可以透過 application 拿到由 Room 所持有的本地端數據,
並在這邊設定 Paging 的 Config,像是每一頁的數量,需不需要 Placeholder 等等...
再把剛剛的 BoundaryCallback 餵給 LivePagedListBuilder

這裡為什麼可以透過 dao 拿到 DataSource.Factory 呢?
多虧了 RoomPaging 整合的關係,直接把 Room 回傳的型態改成 DataSource.Factory 就可以了

@Dao
interface DataItemDao {

    @Query("SELECT * FROM DATA_ITEM_ENTITY")
    fun getAllDataItem(): DataSource.Factory<Int, DataItem>
}

修改完 Repository 後,因為要把資料存進 Room,要透過 ViewModel 把 Context 傳給 Repository 用,還記得之前有一個 AndroidViewModel 可以拿到 Application 的 Context 嗎

class PagingViewModel(repository: PagingRepository, application: Application) :
    AndroidViewModel(application) {

    val pagedListLiveData = repository.getDataItem(application)
}

這樣算是完成了 NetWork + Database 的資料來源,不過在第一次開啟時,因為資料庫沒有任何資料,所以在 BoundaryCallback裡面觸發 onZeroItemsLoaded時會跟 API 要資料,這時候整個 List 會有跳動的感覺, 資料的排列也很奇怪。

但是第二次再開啟時的滑動就變得很順暢,打印 Log 後確定是有存進 Db 裡面的,代表後面這一段應該是有寫對(吧?

如果有人發現了我哪邊寫錯,可以在下方留言告訴我,解不了 Bug 實在是一件很煩躁的事

其他有任何問題或講得不清楚的地方歡迎留言和我討論。

更歡迎留言糾正我任何說錯的地方!

下一篇:Paging (Last) 總結 + 最後心得


上一篇
Day 20 Paging (二) NetWork Datasource
下一篇
Day 22 Paging (Last) 總結 + 最後心得
系列文
Android Architecture Components 學習心得筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言