preface

Google has recently updated several new Jetpack members, Hilt, Paging 3, App Startup, and more.

In the previous article, which analyzed App Startup practice and principle and Paging3 load local data (a) practice and principle, if you have not seen you can click the following address to view:

  • AndroidX App Startup practice and principle analysis of Jetpack’s latest member
  • Jetpack member Paging3 Database practice and source code Analysis (1)
  • Jetpack Member Paging3 Network Practice and Principle Analysis (II)
  • Jetpack member Paging3 uses RemoteMediator to load network page data and update it to the database.
  • Code address: https://github.com/hi-dhl/AndroidX-Jetpack-Practice

Today this article mainly to analyze the Paging3 load network data and its principle, using the weekend time reference Google document to achieve the Paging3 period also encountered some pits, will be detailed analysis in the article, the code has been uploaded to GitHub: Paging3SimpleWithNetWork

In this article you will learn the following:

  • What is Paging3?
  • Changes to the core of Paging3 from previous versions (Paging1, Paging2)?
  • About Paging supported Paging policies?
  • How do I use Paging3 to load network data in a project?
  • How to handle the Paging3 network exception?
  • How does Paging3 listen for network request status?
  • How does Paging3 refresh and retry?

The Paging3SimpleWithNetWork project uses Coil (Kotlin image-loading library), Databinding (Databinding), Anko (mainly used as an alternative to XML), and Koin (Kotlin) In my opinion, dependency injection libraries (DDI), JDatabinding (components packaged on top of Databinding), Data Mapper (Data mapping), versioning using Composing builds as dependency libraries, Repository design patterns, MVVM architecture, and more. Some of these technologies are unknown before, you can click the link below to view.

  • [1.4K+ Star] Picasso
  • [2.4K Star] Drop Dagger to Koin
  • Bye-bye buildSrc, embrace Composing builds for faster Android builds
  • Kotlin + Android Databinding package in the project
  • Few people know Kotlin’s technique and principle analysis

What is Paging3?

Paging is a Paging library that helps you load display data from local storage or over a network. This allows your App to use network bandwidth and system resources more efficiently.

Google recommends using Paging as part of App architecture, which can be easily integrated with Jetpack components. Paging3 contains the following functions:

  • Caching paging data in memory ensures that your App uses system resources efficiently when using paging data.
  • Built-in deduplication requests ensure that your App uses network bandwidth and system resources efficiently.
  • RecyclerView adapters can be configured to automatically request data when the user scrolls to the end of the load data.
  • Support for Kotlin coroutines and flows, as well as LiveData and RxJava.
  • Built-in error handling support, including refresh and retry features.

Function change of Paging3 relative to the previous class

ItemKeyedDataSource, PageKeyedDataSource, and PositionalDataSource classes are provided before Paging3, in which the operation of data acquisition is carried out.

  • PositionalDataSource: Mainly used to load data with limited data (loading local database)
  • ItemKeyedDataSource: Used to request web data. It works with the id of the last item on the current page as the starting point for the next page, as in Github’s API.
    • Such as addresshttps://api.github.com/users?since=0?per_page=30When since = 0 gets the first page of data, the last data on the current page has ID 46.
    • Using 46 as the starting position, since = 46, the address becomes:https://api.github.com/users?since=46?per_page=30.
  • PageKeyedDataSource: also used to request network data, it is suitable to request data through pagination.

After Paging3, ItemKeyedDataSource, PageKeyedDataSource, and PositionalDataSource are merged into one PagingSource. All the old API loading methods are merged into a single load() method in the PagingSource.

abstract suspend fun load(params: LoadParams<Key>): LoadResult<Key, Value>
Copy the code

This is a suspend function that implements this method to trigger asynchronous loading, as described below, plus the following changes in Paging3

  • LivePagedListBuilder and RxPagedListBuilder were merged into Pager.
  • Replace PagingConfig with pagedlist. Config.
  • Using RemoteMediator replaced the PagedList. BoundaryCallback to load the network and the local database data.

Four steps to achieve Paging3 load network data

Google recommends that we operate in three layers of the application when using Paging3, and how they work together to load and display paging data, as shown below:

We next according to Google recommended way to start implementation, only need four steps to achieve Paging3 load network data, the article only posted the core code, the specific implementation can see the GitHub Paging3SimpleWithNetWork project.

1. Network request part

The GitHub API has been chosen

interface GitHubService {

    @GET("users")
    suspend fun getGithubAccount(@Query("since") id: Int, @Query("per_page") perPage: Int):
            List<GithubAccountModel>

    companion object {
        fun create(): GitHubService {
            val client = OkHttpClient.Builder()
                .build()

            val retrofit = Retrofit.Builder()
                .client(client)
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build()

            return retrofit.create(GitHubService::class.java)
        }
    }
}
Copy the code

Note: You need to add the suspend keyword before the getGithubAccount method, otherwise the following exception will be thrown when called.

Unable to create call adapter for XXXXX
Copy the code

2. Create the PagingSource data source in the Repository layer

class GitHubItemPagingSource( private val api: GitHubService ) : PagingSource<Int, GithubAccountModel>(), AnkoLogger { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, GithubAccountModel> {return try {// key equivalent to id val key = params.key? : Val items = api.getGithubAccount(key, params.loadsize) // The request fails or an exception occurs. Error(e) // Request successful, Loadresult. Page returns loadResult. Page(data = items, // prevKey = null, // previous Page) NextKey = items.lastorNULL ()? .id// next page, set to null, no more loading effect, if no more data is set to null, that is, no data is loaded after sliding to the end)} catch (e: Exception) { e.printStackTrace() LoadResult.Error(e) } } }Copy the code
  • PagingSource is an abstract class that provides source data to Paging. The load method needs to be overridden to process network requests. Note the prevKey and nextKey parameters in loadResult. Page.

    • PrevKey = null; prevKey = null; prevKey = null; prevKey = null; prevKey = null; prevKey = null; prevKey = null; prevKey = null;
    • NextKey: Next page, if set to empty, no more effects are loaded. If no more data is set to empty after the slide, no data is loaded.
  • LoadParams is a sealed class with three inner classes Refresh, Append, and Prepend.

    The name of the class role
    Refresh Use of initialization refresh
    Append Used when loading more
    Prepend Used when adding data to the current list header

3. Create Pager and PagingData in the Repository layer

  • Pager: is the main entry page that accepts PagingConfig, initialKey, remoteMediator, pagingSourceFactory in its constructor.
  • PagingData: is a container for PagingData that queries a PagingSource object and stores the results.
class GitHubRepositoryImpl( val pageConfig: PagingConfig, val gitHubApi: GitHubService, val mapper2Person: Mapper<GithubAccountModel, GitHubAccount> ) : Repository { override fun postOfData(id: Int): Flow<PagingData<GitHubAccount>> {return Pager(pageConfig) {// Load database data GitHubItemPagingSource(gitHubApi, 0)}.flow.map {pagingData -> // GitHubAccount pagingdata.map {mapper2person.map (it)}}}}Copy the code

We build a Pager in the postOfData method that accepts PagingConfig, initialKey, remoteMediator, pagingSourceFactory, InitialKey and remoteMediator are optional, pageConfig and pagingSourceFactory are mandatory.

PagingSourceFactory is a lambda expression that can be expressed directly in curly braces in Kotlin, where the network request GitHubItemPagingSource(gitHubApi, 0) is executed.

Finally, call flow to return flow >, and then convert the data source GithubAccountModel into GithubAccount used by the upper layer through flow map method.

Flow has been analyzed in the previous Jetpack member Paging3 practice and source code analysis (I).

4. The last step is to accept the data and bind the UI

The data is received in the ViewModel and passed to the Adapter.

val gitHubLiveData: LiveData<PagingData<GitHubAccount>> =
        repository.postOfData(0).asLiveData()
Copy the code

There are three ways to use LiveData, one of which is demonstrated here, and the rest were analyzed in the previous article Jetpack member Paging3 practices and source code analysis (I).

 mMainViewModel.gitHubLiveData.observe(this, Observer { data ->
            mAdapter.submitData(lifecycle, data)
        })
Copy the code

At this point, the request for network data and display ends up on the UI. Finally, let’s take a look at Paging3’s built-in error handling support, including refreshing and retry functions.

5. Handle the abnormal network status

Paging3 provides built-in error handling support, including refresh and retry functions, said here Google for Paging3 design compared to the previous design is really good, basically network request local RecyclerView to display data, Refresh, retry, error handling, and so on are required.

1. Error handling

The component of Paging3, PagingDataAdapter, is a returnable view adapter that handles paging data. PagingDataAdapter provides three methods, as shown in the figure below:

The method name role
withLoadStateFooter Add the bottom of the list (similar to loading more)
withLoadStateHeader Add the head of the list
withLoadStateHeaderAndFooter Add a header and a bottom

Paging3 provides a LoadStateAdapter for implementing the bottom and header styles of the list. You just need to inherit the LoadStateAdapter to handle the corresponding network state, such as the FooterAdapter implemented here to load more material.

class FooterAdapter(val adapter: GitHubAdapter) : LoadStateAdapter<NetworkStateItemViewHolder>() { override fun onBindViewHolder(holder: NetworkStateItemViewHolder, loadState: LoadState) { holder.bindData(loadState, 0) } override fun onCreateViewHolder( parent: ViewGroup, loadState: LoadState ): NetworkStateItemViewHolder { val view = inflateView(parent, R.layout.recycie_item_network_state) return NetworkStateItemViewHolder(view) { adapter.retry() } } private fun inflateView(viewGroup: ViewGroup, @LayoutRes viewType: Int): View { val layoutInflater = LayoutInflater.from(viewGroup.context) return layoutInflater.inflate(viewType, viewGroup, false) } } class NetworkStateItemViewHolder(view: View, private val retryCallback: () -> Unit) : DataBindingViewHolder<LoadState>(view) { val mBinding: RecycieItemNetworkStateBinding by viewHolderBinding(view) override fun bindData(data: LoadState, position: Int) {mbinding. apply {progressbar. isVisible = data is LoadState. Display and click the retry button retryButton. IsVisible = data is LoadState. Error retryButton. SetOnClickListener {retryCallback ()} / / Load failed to display error cause errormsg. isVisible =! (data as? LoadState.Error)? .error? .message.isNullOrBlank() errorMsg.text = (data as? LoadState.Error)? .error? .message executePendingBindings() } } }Copy the code

The states are handled separately above, loading, loading failed, providing retry buttons, and so on.

2. Paging3 also provides methods to refresh, retry, and so on, as shown below:

  • Refresh: Used to drop down data updates.
  • Retry: Used for more on the bottom, display the retry button and click to invoke retry when the request network fails.

3. Paging3 also helps me to deal with if there are multiple network requests, only the last request will be processed, for example, due to the slow network, users frequently refresh data and so on

6. Monitor the network request status

You just analyzed that the PagingDataAdapter is a recyclable view adapter that handles paging data and also provides two methods to listen for the state of the data.

The differences between the two methods are:

  • AddDataRefreshListener: Called when a new PagingData is submitted and displayed.
  • AddLoadStateListener: This method differs from the addDataRefreshListener method in that the addLoadStateListener method returns a CombinedLoadStates object, as shown in the figure above.

CombinedLoadStates is a data class with three member variables refresh, prepend, and Append.

val refresh: LoadState = (mediator ? : source).refresh val prepend: LoadState = (mediator ? : source).prepend val append: LoadState = (mediator ? : source).appendCopy the code
variable role
refresh Use of initialization refresh
append Used when loading more
prepend Used when adding data to the current list header

Refresh, prepend, and Append are all objects of LoadState. LoadState is also a sealed class, and each refresh, prepend, and Append corresponds to three states.

variable role
Error Loading failed
Loading Loading
NotLoading Indicates that the current load is not loaded

Here I have to admire Google for thinking of everything for us, here needs to be combined with their own project actual situation, to customize different state handling.

Paging3SimpleWithNetWork has been uploaded to GitHub. Finally, I wish you all a nice weekend.

Plan to establish a most complete and latest AndroidX Jetpack related components of the actual combat project and related components of the principle of analysis article, is gradually increasing Jetpack new members, the warehouse continues to update, you can go to check: Androidx-jetpack-practice, if this warehouse is helpful to you, please give me a thumbs up and I will finish more project practices for new members of Jetpack one after another.

conclusion

Committed to share a series of Android system source code, reverse analysis, algorithm, translation, Jetpack source related articles, is trying to write a better article, if this article is helpful to you to give a star, to learn together, looking forward to growing with you.

algorithm

Since LeetCode has a large question bank, hundreds of questions can be selected for each category. Due to the limited energy of each person, it is impossible to brush all the questions. Therefore, I sorted the questions according to the classic types and the difficulty of the questions.

  • Data structures: arrays, stacks, queues, strings, linked lists, trees…
  • Algorithms: Search algorithm, search algorithm, bit operation, sorting, mathematics,…

Each problem will be implemented in Java and Kotlin, and each problem has a solution idea. If you like algorithms and LeetCode like me, you can pay attention to my Solution of LeetCode problem on GitHub: Leetcode-Solutions-with-Java-And-Kotlin, come to learn together And look forward to growing with you.

Android 10 source code series

I’m writing a series of Android 10 source code analysis articles. Knowing the system source code is not only helpful in analyzing problems, but also very helpful in the interview process. If you like to study Android source code as MUCH as I do, You can follow my Android10-source-Analysis on GitHub, and all articles will be synchronized to this repository.

  • How is APK generated
  • APK installation process
  • 0xA03 Android 10 source code analysis: APK loading process of resource loading
  • Android 10 source code: APK
  • Dialog loading and drawing process and use in Kotlin, DataBinding
  • WindowManager View binding and architecture
  • 0xA07 Android 10 source code analysis: Window type and 3d view hierarchy analysis
  • More……

Android Apps

  • How to package Kotlin + Android Databinding in your project
  • Bye-bye buildSrc, embrace Composing builds for faster Android builds
  • Few people know Kotlin’s technique and principle analysis
  • AndroidX App Startup practice and principle analysis of Jetpack’s latest member
  • Jetpack member Paging3 Practice and Source Code Analysis (PART 1)

Select a translation

At present, I am sorting out and translating a series of selected foreign technical articles. Besides translation, many excellent English technical articles provide good ideas and methods. Every article has a part of the translator’s thinking and a deeper interpretation of the original text. You can pay attention to my Technical-Article-Translation on GitHub, and articles will be synchronized to this warehouse.

  • [Google engineers] just released a new Fragment feature, “New ways to transfer Data between Fragments” and source code analysis
  • How does FragmentFactory elegantly use Koin and partial source code analysis
  • [2.4K Start] Drop Dagger to Koin
  • [5K +] Kotlin’s performance optimization stuff
  • Decrypt RxJava’s exception handling mechanism
  • [1.4K+ Star] Picasso
  • More……

Tool series

  • Shortcuts to AndroidStudio that few people know
  • Shortcuts to AndroidStudio that few people know
  • All you need to know about ADB commands
  • 10 minutes introduction to Shell scripting
  • Dynamically debug APP based on Smali file Android Studio
  • The Android Device Monitor tool cannot be found in Android Studio 3.2