SlideShare a Scribd company logo
1 of 37
Paging Like A Pro
Android Architecture Components: Paging
Gabor Varadi
@zhuinden
Recap: What’s in the AAC?
• Lifecycle (LifecycleObserver, @OnLifecycleEvent)
• ViewModel (ViewModelProviders, ViewModelStore)
• LiveData (liveData.observe(lifecycleOwner,
observer))
• Room (ORM - @Entity, @ForeignKey, etc.)
What’s the end game?
Simplified data loading:
- if database exposes an observable query
which is re-queried when a write „invalidates”
the table, then we don’t need to manage „data
loading callbacks”
- threading is simplified: data loading is
automatic and occurs on a background thread
(initial evaluation + triggered by future writes)
How do the pieces fit together?
• Room exposes query result as LiveData (which
retains previous value and can be observed) and updates it
• ViewModel caches the LiveData across
configuration changes
• LiveData pauses operations after onStop until
onStart via Lifecycle events, and automatically
unsubscribes on onDestroy, emits new values
Where’s the catch?
• LiveData<List<T>>
@Dao
interface CatDao {
@Query("SELECT * FROM ${Cat.TABLE_NAME}
ORDER BY ${Cat.COLUMN_RANK}")
fun listenForCats(): LiveData<List<Cat>>
}
• What if I have MANY (10000+) cats?
• What if I update it often?
• How many items should I read to memory?
Should I read all items when a write happens?
Possible solutions:
• 1.) implement pagination manually:
„load more” button
• 2.) lazy-load everything!!1!
• 3.) ???
1.) Paginating manually
• „Load more” callbacks: appending to already
loaded list
• Doesn’t listen for database writes
automatically at all
• Must load and keep all previous items in
memory
2.) Lazy loading
• does this approach
• Every query result is a lazy-loaded „cursor”
• Cursors are invalidated and re-calculated on
writes, making „listening for changes” possible
• Everything is a proxy, no data is kept in
memory – everything is loaded from DB
private Realm realm;
private RealmResults<City> cities;
private RealmChangeListener<RealmResults<City>>
realmChangeListener = cities -> {
adapter.setData(cities);
};
realm = Realm.getDefaultInstance();
cities = realm.where(City.class).findAllAsync();
cities.addChangeListener(realmChangeListener);
// ...
cities.removeAllChangeListeners();
realm.close();
2.) Lazy loading
• Downside:
– Evaluation of the „lazy” data set must still
evaluate the entirety of the cursor
(in SQLite’s case, only a window is filled up, but
subsequent loads would happen on UI thread)
– Query result is evaluated on background thread,
but in Realm’s case, the accessors are on main
thread, and it is not a completely free operation
3.) ???
3.) Black magic
3.) Async loading pages of a data
source
• a.) AsyncListUtil: added in support v24.1.0
• b.) Paging Library: currently 1.0.0-RC1
3.b) PagedList + DataSource
• Idea:
– only load small pages to memory that we actually need
– reading the pages (and elements) should occur on
background thread
• Paging Library gives us:
– PagedList: exposes data
– DataSource: fills paged list
– DataSource.Factory: creates a data source
– PagedListAdapter: consumes paged list
– LivePagedListBuilder: creates new datasource
after invalidation
DataSource types
• Positional: pages can be indexed [0...n-1],
then [n, n+1, ...]
• ItemKeyed: item in page at index[n-1] has a
value that allows us to load the page that
contains [n, n+1, ...]
• PageKeyed: page contains a „cursor” value
that allows us to request „the next page”
Positional data sources
• Used for accessing data that supports
limit+offset (skip+take)
• Most common usage is to fill the paged list
from a database
• (Room has its own: LimitOffsetDataSource)
Can we still listen for changes?
• DataSource can be invalidated
• With a DataSource.Factory, the
DataSource can be re-created when it is
invalidated
• (Invalidation happens when a write has
changed the data set in such a way that a re-
evaluation is required)
How do I listen for changes?
private val liveResults: LiveData<PagedList<Cat>>
fun getCats() = liveResults
class TaskViewModel: ViewModel() {
init {
...
liveResults = LivePagedListBuilder<>(
catDao.listenForCats(),
PagedList.Config.Builder()
.setPageSize(20)
.setPrefetchDistance(20)
.setEnablePlaceholders(true)
.build())
.setInitialLoadKey(0)
.build()
}
Listening for changes
@Dao
interface CatDao {
@Query("SELECT * FROM ${Cat.TABLE_NAME}
ORDER BY ${Cat.COLUMN_RANK}")
fun listenForCats(): DataSource.Factory<Int, Cat>
}
class CatFragment: Fragment() {
override fun onViewCreated(view: View, icicle: Bundle?) {
...
val viewModel = ViewModelProviders.of(this)
.get<CatViewModel>();
...
recyclerView.setAdapter(catAdapter);
viewModel.getCats().observe(this) { pagedList ->
catAdapter.submitList(pagedList)
}
}
}
class CatAdapter: PagedListAdapter<Cat,
CatAdapter.ViewHolder>(Cat.DIFF_CALLBACK) {
override fun onBindViewHolder(holder: ViewHolder, pos: Int) {
val cat = getItem(pos);
if(cat != null) { // null makes datasource fetch page
holder.bind(cat);
}
}
...
}
What about network requests?
• Typically, we must fetch new items from the
server when we’re scrolling down to the
bottom (we’ve reached the end of our data
set)
• Solution: PagedList.BoundaryCallback
PagedList.BoundaryCallback
• onItemAtEndLoaded(T itemAtEnd)
• onItemAtFrontLoaded(T itemAtFront)
• onZeroItemsLoaded()
• This is where we can start network requests to fetch the
next batch of cats when we’ve reached the end of what’s
stored in the database
• The callback can be called multiple times, so we should
ensure that we don’t execute the same network request
multiple times
• We can set this on the LivePagedListBuilder
Custom Datasources
• Each data source type has a
LoadInitialCallback and a
LoadCallback
(or LoadRangeCallback for positional)
• We can extend PositionalDataSource,
ItemKeyedDataSource or
PageKeyedDataSource, and implement
the right methods
ItemKeyed / PagedKeyed Datasources
• It is possible to fetch pages from network, and
keep them in memory (without storing them
in the database)
• The keyed data sources make this use-case
easier
Load callbacks
• PagedKeyedDataSource:
– Initial load callback:
public abstract void onResult(
@NonNull List<Value> data,
@Nullable Key previousPageKey,
@Nullable Key nextPageKey);
– Load callback:
public abstract void onResult(
@NonNull List<Value> data,
@Nullable Key adjacentPageKey);
// adjacent is „next” or „previous”
depending on „loadAfter” or „loadBefore”
Load callbacks
• ItemKeyedDataSource:
– Initial load callback:
public abstract void onResult(
@NonNull List<Value> data,
int position, int totalCount);
– Load callback:
public abstract void onResult(
@NonNull List<Value> data);
– Also: it also has Key getKey(T item) that lets us
obtain the item key as a load parameter
override fun loadAfter(
params: LoadParams<String>,
callback: LoadCallback<String, Cat>) {
val response = catApi.getNextCats(params.key)
.execute()
val body = response.body()
callback.onResult(body.cats, body.after)
}
override fun loadInitial(
params: LoadInitialParams<String>,
callback: LoadInitialCallback<String, Cat>) {
val response = catApi.getCats().execute()
val body = response.body()
callback.onResult(body.cats,
body.before, body.after)
}
Pull to refresh?
• If we expose our keyed data source via a
DataSource.Factory that is wrapped in a
LivePagedListBuilder, then we can
invalidate the data source, and re-retrieve the
initial page
class CatDataSourceFactory(
private val catApi: CatApi
) : DataSource.Factory<String, Cat>() {
override fun create(): DataSource<String, Cat> =
CatDataSource(catApi)
}
Network load status?
• We might want to show a loading indicator while
the datasource is retrieving data (especially if
downloading from network)
• Solution:
– we need to expose the „latest loading status” of „the
latest data source” that was created by the factory
– The data source can expose status via LiveData
– The factory can expose data source via LiveData
class CatDataSourceFactory(
private val catApi: CatApi
) : DataSource.Factory<String, Cat>() {
val lastDataSource =
MutableLiveData<CatDataSource>()
override fun create(): DataSource<String, Cat> =
CatDataSource(catApi).also { source ->
lastDataSource.postValue(source)
}
}
Transformations.switchMap(srcFactory.lastDataSource){
source ->
source.networkState
}
class CatDataSource(
private val catApi: CatApi
) : PageKeyedDataSource<String, Cat>() {
val networkState: MutableLiveData<NetworkState>()
override fun loadAfter(
params: LoadParams<String>,
callback: LoadCallback<String, Cat>) {
networkState.postValue(NetworkState.LOADING)
val response = catApi.getNextCats(params.key)
.execute()
val body = response.body()
networkState.postValue(NetworkState.SUCCESS)
callback.onResult(body.cats, body.after)
}
}
Retry request?
• As we made data source factory expose latest data
source, it is possible to expose a callback that fetches the
next page
Retry:
dataSourceFactory.latestLiveData.value?
.retry()
Refresh:
dataSourceFactory.latestLiveData.value?
.refresh() // invalidate
Error handling?
• Similarly to exposing load state, we can
expose “current error value” as a LiveData
from the DataSource
• Transformations.switchMap() from the
DataSource within the DataSource.Factory’s
LiveData, again
We have everything! 
The end game
• Room exposes DataSource.Factory<Key, T>, the
entity provides DiffUtil.ItemCallback
• ViewModel holds DataSource.Factory, builds
LiveData<PagedList<T>> using
LivePagedListProvider (with provided
PagedList.BoundaryCallback for fetching
new data), exposes DataSource’s LiveDatas via
switchMap()
• Fragment observes LiveData exposed from
ViewModel, feeds PagedList to
PagedListAdapter
Additional resources
https://github.com/googlesamples/android-
architecture-components/
https://developer.android.com/topic/libraries/ar
chitecture/guide
Thank you for your attention!
Q/A?

More Related Content

What's hot

Android | Android Activity Launch Modes and Tasks | Gonçalo Silva
Android | Android Activity Launch Modes and Tasks | Gonçalo SilvaAndroid | Android Activity Launch Modes and Tasks | Gonçalo Silva
Android | Android Activity Launch Modes and Tasks | Gonçalo SilvaJAX London
 
PHP POSTGRESQL integration
PHP POSTGRESQL  integrationPHP POSTGRESQL  integration
PHP POSTGRESQL integrationSandhya Sharma
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Domenic Denicola
 
Spring Framework - AOP
Spring Framework - AOPSpring Framework - AOP
Spring Framework - AOPDzmitry Naskou
 
Mocking in Java with Mockito
Mocking in Java with MockitoMocking in Java with Mockito
Mocking in Java with MockitoRichard Paul
 
Spring - Part 1 - IoC, Di and Beans
Spring - Part 1 - IoC, Di and Beans Spring - Part 1 - IoC, Di and Beans
Spring - Part 1 - IoC, Di and Beans Hitesh-Java
 
Présentation de ECMAScript 6
Présentation de ECMAScript 6Présentation de ECMAScript 6
Présentation de ECMAScript 6Julien CROUZET
 
Devoxx France 2023 - Les nouveautés de Java 19 et 20
Devoxx France 2023 - Les nouveautés de Java 19 et 20Devoxx France 2023 - Les nouveautés de Java 19 et 20
Devoxx France 2023 - Les nouveautés de Java 19 et 20Jean-Michel Doudoux
 
Spring Boot Actuator
Spring Boot ActuatorSpring Boot Actuator
Spring Boot ActuatorRowell Belen
 
Workshop spring session 2 - La persistance au sein des applications Java
Workshop spring   session 2 - La persistance au sein des applications JavaWorkshop spring   session 2 - La persistance au sein des applications Java
Workshop spring session 2 - La persistance au sein des applications JavaAntoine Rey
 
Notions de base de JavaScript
Notions de base de JavaScriptNotions de base de JavaScript
Notions de base de JavaScriptKristen Le Liboux
 
Top 10 reasons to migrate to Gradle
Top 10 reasons to migrate to GradleTop 10 reasons to migrate to Gradle
Top 10 reasons to migrate to GradleStrannik_2013
 

What's hot (20)

Android | Android Activity Launch Modes and Tasks | Gonçalo Silva
Android | Android Activity Launch Modes and Tasks | Gonçalo SilvaAndroid | Android Activity Launch Modes and Tasks | Gonçalo Silva
Android | Android Activity Launch Modes and Tasks | Gonçalo Silva
 
PHP POSTGRESQL integration
PHP POSTGRESQL  integrationPHP POSTGRESQL  integration
PHP POSTGRESQL integration
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Spring boot
Spring bootSpring boot
Spring boot
 
Spring Framework - AOP
Spring Framework - AOPSpring Framework - AOP
Spring Framework - AOP
 
Java 8 date & time api
Java 8 date & time apiJava 8 date & time api
Java 8 date & time api
 
Mocking in Java with Mockito
Mocking in Java with MockitoMocking in Java with Mockito
Mocking in Java with Mockito
 
Spring - Part 1 - IoC, Di and Beans
Spring - Part 1 - IoC, Di and Beans Spring - Part 1 - IoC, Di and Beans
Spring - Part 1 - IoC, Di and Beans
 
JavaScript Promises
JavaScript PromisesJavaScript Promises
JavaScript Promises
 
Présentation de ECMAScript 6
Présentation de ECMAScript 6Présentation de ECMAScript 6
Présentation de ECMAScript 6
 
Devoxx France 2023 - Les nouveautés de Java 19 et 20
Devoxx France 2023 - Les nouveautés de Java 19 et 20Devoxx France 2023 - Les nouveautés de Java 19 et 20
Devoxx France 2023 - Les nouveautés de Java 19 et 20
 
Spring Boot Actuator
Spring Boot ActuatorSpring Boot Actuator
Spring Boot Actuator
 
Workshop spring session 2 - La persistance au sein des applications Java
Workshop spring   session 2 - La persistance au sein des applications JavaWorkshop spring   session 2 - La persistance au sein des applications Java
Workshop spring session 2 - La persistance au sein des applications Java
 
Notions de base de JavaScript
Notions de base de JavaScriptNotions de base de JavaScript
Notions de base de JavaScript
 
Top 10 reasons to migrate to Gradle
Top 10 reasons to migrate to GradleTop 10 reasons to migrate to Gradle
Top 10 reasons to migrate to Gradle
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
React hooks
React hooksReact hooks
React hooks
 

Similar to Paging Like A Pro

CBStreams - Java Streams for ColdFusion (CFML)
CBStreams - Java Streams for ColdFusion (CFML)CBStreams - Java Streams for ColdFusion (CFML)
CBStreams - Java Streams for ColdFusion (CFML)Ortus Solutions, Corp
 
ITB2019 CBStreams : Accelerate your Functional Programming with the power of ...
ITB2019 CBStreams : Accelerate your Functional Programming with the power of ...ITB2019 CBStreams : Accelerate your Functional Programming with the power of ...
ITB2019 CBStreams : Accelerate your Functional Programming with the power of ...Ortus Solutions, Corp
 
Reactive programming on Android
Reactive programming on AndroidReactive programming on Android
Reactive programming on AndroidTomáš Kypta
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejsNick Lee
 
Android Automated Testing
Android Automated TestingAndroid Automated Testing
Android Automated Testingroisagiv
 
Interactive Kafka Streams
Interactive Kafka StreamsInteractive Kafka Streams
Interactive Kafka Streamsconfluent
 
Rethinking Syncing at AltConf 2019
Rethinking Syncing at AltConf 2019Rethinking Syncing at AltConf 2019
Rethinking Syncing at AltConf 2019Joe Keeley
 
Building Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJavaBuilding Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJavaRick Warren
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196Mahmoud Samir Fayed
 
Lift 2 0
Lift 2 0Lift 2 0
Lift 2 0SO
 
Javascript Application Architecture with Backbone.JS
Javascript Application Architecture with Backbone.JSJavascript Application Architecture with Backbone.JS
Javascript Application Architecture with Backbone.JSMin Ming Lo
 
Painless Persistence in a Disconnected World
Painless Persistence in a Disconnected WorldPainless Persistence in a Disconnected World
Painless Persistence in a Disconnected WorldChristian Melchior
 
Spring Day | Spring and Scala | Eberhard Wolff
Spring Day | Spring and Scala | Eberhard WolffSpring Day | Spring and Scala | Eberhard Wolff
Spring Day | Spring and Scala | Eberhard WolffJAX London
 
Sharepoint Saturday India Online best practice for developing share point sol...
Sharepoint Saturday India Online best practice for developing share point sol...Sharepoint Saturday India Online best practice for developing share point sol...
Sharepoint Saturday India Online best practice for developing share point sol...Shakir Majeed Khan
 
Lazy vs. Eager Loading Strategies in JPA 2.1
Lazy vs. Eager Loading Strategies in JPA 2.1Lazy vs. Eager Loading Strategies in JPA 2.1
Lazy vs. Eager Loading Strategies in JPA 2.1Patrycja Wegrzynowicz
 
[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기NAVER D2
 
Rapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and KtorRapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and KtorTrayan Iliev
 

Similar to Paging Like A Pro (20)

Kotlin talk
Kotlin talkKotlin talk
Kotlin talk
 
CBStreams - Java Streams for ColdFusion (CFML)
CBStreams - Java Streams for ColdFusion (CFML)CBStreams - Java Streams for ColdFusion (CFML)
CBStreams - Java Streams for ColdFusion (CFML)
 
ITB2019 CBStreams : Accelerate your Functional Programming with the power of ...
ITB2019 CBStreams : Accelerate your Functional Programming with the power of ...ITB2019 CBStreams : Accelerate your Functional Programming with the power of ...
ITB2019 CBStreams : Accelerate your Functional Programming with the power of ...
 
Spring data requery
Spring data requerySpring data requery
Spring data requery
 
Requery overview
Requery overviewRequery overview
Requery overview
 
Reactive programming on Android
Reactive programming on AndroidReactive programming on Android
Reactive programming on Android
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
Android Automated Testing
Android Automated TestingAndroid Automated Testing
Android Automated Testing
 
Interactive Kafka Streams
Interactive Kafka StreamsInteractive Kafka Streams
Interactive Kafka Streams
 
Rethinking Syncing at AltConf 2019
Rethinking Syncing at AltConf 2019Rethinking Syncing at AltConf 2019
Rethinking Syncing at AltConf 2019
 
Building Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJavaBuilding Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJava
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196
 
Lift 2 0
Lift 2 0Lift 2 0
Lift 2 0
 
Javascript Application Architecture with Backbone.JS
Javascript Application Architecture with Backbone.JSJavascript Application Architecture with Backbone.JS
Javascript Application Architecture with Backbone.JS
 
Painless Persistence in a Disconnected World
Painless Persistence in a Disconnected WorldPainless Persistence in a Disconnected World
Painless Persistence in a Disconnected World
 
Spring Day | Spring and Scala | Eberhard Wolff
Spring Day | Spring and Scala | Eberhard WolffSpring Day | Spring and Scala | Eberhard Wolff
Spring Day | Spring and Scala | Eberhard Wolff
 
Sharepoint Saturday India Online best practice for developing share point sol...
Sharepoint Saturday India Online best practice for developing share point sol...Sharepoint Saturday India Online best practice for developing share point sol...
Sharepoint Saturday India Online best practice for developing share point sol...
 
Lazy vs. Eager Loading Strategies in JPA 2.1
Lazy vs. Eager Loading Strategies in JPA 2.1Lazy vs. Eager Loading Strategies in JPA 2.1
Lazy vs. Eager Loading Strategies in JPA 2.1
 
[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기[245] presto 내부구조 파헤치기
[245] presto 내부구조 파헤치기
 
Rapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and KtorRapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and Ktor
 

Recently uploaded

call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...harshavardhanraghave
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfCionsystems
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....ShaimaaMohamedGalal
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 

Recently uploaded (20)

call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Exploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the ProcessExploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the Process
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdf
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
Clustering techniques data mining book ....
Clustering techniques data mining book ....Clustering techniques data mining book ....
Clustering techniques data mining book ....
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 

Paging Like A Pro

  • 1. Paging Like A Pro Android Architecture Components: Paging Gabor Varadi @zhuinden
  • 2. Recap: What’s in the AAC? • Lifecycle (LifecycleObserver, @OnLifecycleEvent) • ViewModel (ViewModelProviders, ViewModelStore) • LiveData (liveData.observe(lifecycleOwner, observer)) • Room (ORM - @Entity, @ForeignKey, etc.)
  • 3. What’s the end game? Simplified data loading: - if database exposes an observable query which is re-queried when a write „invalidates” the table, then we don’t need to manage „data loading callbacks” - threading is simplified: data loading is automatic and occurs on a background thread (initial evaluation + triggered by future writes)
  • 4. How do the pieces fit together? • Room exposes query result as LiveData (which retains previous value and can be observed) and updates it • ViewModel caches the LiveData across configuration changes • LiveData pauses operations after onStop until onStart via Lifecycle events, and automatically unsubscribes on onDestroy, emits new values
  • 5. Where’s the catch? • LiveData<List<T>> @Dao interface CatDao { @Query("SELECT * FROM ${Cat.TABLE_NAME} ORDER BY ${Cat.COLUMN_RANK}") fun listenForCats(): LiveData<List<Cat>> } • What if I have MANY (10000+) cats? • What if I update it often? • How many items should I read to memory? Should I read all items when a write happens?
  • 6. Possible solutions: • 1.) implement pagination manually: „load more” button • 2.) lazy-load everything!!1! • 3.) ???
  • 7. 1.) Paginating manually • „Load more” callbacks: appending to already loaded list • Doesn’t listen for database writes automatically at all • Must load and keep all previous items in memory
  • 8. 2.) Lazy loading • does this approach • Every query result is a lazy-loaded „cursor” • Cursors are invalidated and re-calculated on writes, making „listening for changes” possible • Everything is a proxy, no data is kept in memory – everything is loaded from DB
  • 9. private Realm realm; private RealmResults<City> cities; private RealmChangeListener<RealmResults<City>> realmChangeListener = cities -> { adapter.setData(cities); }; realm = Realm.getDefaultInstance(); cities = realm.where(City.class).findAllAsync(); cities.addChangeListener(realmChangeListener); // ... cities.removeAllChangeListeners(); realm.close();
  • 10. 2.) Lazy loading • Downside: – Evaluation of the „lazy” data set must still evaluate the entirety of the cursor (in SQLite’s case, only a window is filled up, but subsequent loads would happen on UI thread) – Query result is evaluated on background thread, but in Realm’s case, the accessors are on main thread, and it is not a completely free operation
  • 13. 3.) Async loading pages of a data source • a.) AsyncListUtil: added in support v24.1.0 • b.) Paging Library: currently 1.0.0-RC1
  • 14. 3.b) PagedList + DataSource • Idea: – only load small pages to memory that we actually need – reading the pages (and elements) should occur on background thread • Paging Library gives us: – PagedList: exposes data – DataSource: fills paged list – DataSource.Factory: creates a data source – PagedListAdapter: consumes paged list – LivePagedListBuilder: creates new datasource after invalidation
  • 15. DataSource types • Positional: pages can be indexed [0...n-1], then [n, n+1, ...] • ItemKeyed: item in page at index[n-1] has a value that allows us to load the page that contains [n, n+1, ...] • PageKeyed: page contains a „cursor” value that allows us to request „the next page”
  • 16. Positional data sources • Used for accessing data that supports limit+offset (skip+take) • Most common usage is to fill the paged list from a database • (Room has its own: LimitOffsetDataSource)
  • 17. Can we still listen for changes? • DataSource can be invalidated • With a DataSource.Factory, the DataSource can be re-created when it is invalidated • (Invalidation happens when a write has changed the data set in such a way that a re- evaluation is required)
  • 18. How do I listen for changes? private val liveResults: LiveData<PagedList<Cat>> fun getCats() = liveResults class TaskViewModel: ViewModel() { init { ... liveResults = LivePagedListBuilder<>( catDao.listenForCats(), PagedList.Config.Builder() .setPageSize(20) .setPrefetchDistance(20) .setEnablePlaceholders(true) .build()) .setInitialLoadKey(0) .build() }
  • 19. Listening for changes @Dao interface CatDao { @Query("SELECT * FROM ${Cat.TABLE_NAME} ORDER BY ${Cat.COLUMN_RANK}") fun listenForCats(): DataSource.Factory<Int, Cat> }
  • 20. class CatFragment: Fragment() { override fun onViewCreated(view: View, icicle: Bundle?) { ... val viewModel = ViewModelProviders.of(this) .get<CatViewModel>(); ... recyclerView.setAdapter(catAdapter); viewModel.getCats().observe(this) { pagedList -> catAdapter.submitList(pagedList) } } } class CatAdapter: PagedListAdapter<Cat, CatAdapter.ViewHolder>(Cat.DIFF_CALLBACK) { override fun onBindViewHolder(holder: ViewHolder, pos: Int) { val cat = getItem(pos); if(cat != null) { // null makes datasource fetch page holder.bind(cat); } } ... }
  • 21. What about network requests? • Typically, we must fetch new items from the server when we’re scrolling down to the bottom (we’ve reached the end of our data set) • Solution: PagedList.BoundaryCallback
  • 22. PagedList.BoundaryCallback • onItemAtEndLoaded(T itemAtEnd) • onItemAtFrontLoaded(T itemAtFront) • onZeroItemsLoaded() • This is where we can start network requests to fetch the next batch of cats when we’ve reached the end of what’s stored in the database • The callback can be called multiple times, so we should ensure that we don’t execute the same network request multiple times • We can set this on the LivePagedListBuilder
  • 23. Custom Datasources • Each data source type has a LoadInitialCallback and a LoadCallback (or LoadRangeCallback for positional) • We can extend PositionalDataSource, ItemKeyedDataSource or PageKeyedDataSource, and implement the right methods
  • 24. ItemKeyed / PagedKeyed Datasources • It is possible to fetch pages from network, and keep them in memory (without storing them in the database) • The keyed data sources make this use-case easier
  • 25. Load callbacks • PagedKeyedDataSource: – Initial load callback: public abstract void onResult( @NonNull List<Value> data, @Nullable Key previousPageKey, @Nullable Key nextPageKey); – Load callback: public abstract void onResult( @NonNull List<Value> data, @Nullable Key adjacentPageKey); // adjacent is „next” or „previous” depending on „loadAfter” or „loadBefore”
  • 26. Load callbacks • ItemKeyedDataSource: – Initial load callback: public abstract void onResult( @NonNull List<Value> data, int position, int totalCount); – Load callback: public abstract void onResult( @NonNull List<Value> data); – Also: it also has Key getKey(T item) that lets us obtain the item key as a load parameter
  • 27. override fun loadAfter( params: LoadParams<String>, callback: LoadCallback<String, Cat>) { val response = catApi.getNextCats(params.key) .execute() val body = response.body() callback.onResult(body.cats, body.after) } override fun loadInitial( params: LoadInitialParams<String>, callback: LoadInitialCallback<String, Cat>) { val response = catApi.getCats().execute() val body = response.body() callback.onResult(body.cats, body.before, body.after) }
  • 28. Pull to refresh? • If we expose our keyed data source via a DataSource.Factory that is wrapped in a LivePagedListBuilder, then we can invalidate the data source, and re-retrieve the initial page class CatDataSourceFactory( private val catApi: CatApi ) : DataSource.Factory<String, Cat>() { override fun create(): DataSource<String, Cat> = CatDataSource(catApi) }
  • 29. Network load status? • We might want to show a loading indicator while the datasource is retrieving data (especially if downloading from network) • Solution: – we need to expose the „latest loading status” of „the latest data source” that was created by the factory – The data source can expose status via LiveData – The factory can expose data source via LiveData
  • 30. class CatDataSourceFactory( private val catApi: CatApi ) : DataSource.Factory<String, Cat>() { val lastDataSource = MutableLiveData<CatDataSource>() override fun create(): DataSource<String, Cat> = CatDataSource(catApi).also { source -> lastDataSource.postValue(source) } } Transformations.switchMap(srcFactory.lastDataSource){ source -> source.networkState }
  • 31. class CatDataSource( private val catApi: CatApi ) : PageKeyedDataSource<String, Cat>() { val networkState: MutableLiveData<NetworkState>() override fun loadAfter( params: LoadParams<String>, callback: LoadCallback<String, Cat>) { networkState.postValue(NetworkState.LOADING) val response = catApi.getNextCats(params.key) .execute() val body = response.body() networkState.postValue(NetworkState.SUCCESS) callback.onResult(body.cats, body.after) } }
  • 32. Retry request? • As we made data source factory expose latest data source, it is possible to expose a callback that fetches the next page Retry: dataSourceFactory.latestLiveData.value? .retry() Refresh: dataSourceFactory.latestLiveData.value? .refresh() // invalidate
  • 33. Error handling? • Similarly to exposing load state, we can expose “current error value” as a LiveData from the DataSource • Transformations.switchMap() from the DataSource within the DataSource.Factory’s LiveData, again
  • 35. The end game • Room exposes DataSource.Factory<Key, T>, the entity provides DiffUtil.ItemCallback • ViewModel holds DataSource.Factory, builds LiveData<PagedList<T>> using LivePagedListProvider (with provided PagedList.BoundaryCallback for fetching new data), exposes DataSource’s LiveDatas via switchMap() • Fragment observes LiveData exposed from ViewModel, feeds PagedList to PagedListAdapter
  • 37. Thank you for your attention! Q/A?

Editor's Notes

  1. FragmentActivity and support Fragments implement LifecycleOwner, which allows them to expose a lifecycle that can be observed: LifecycleObserver and @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) ViewModel relies on the ViewModelStore (Map<String, ViewModel>) which is kept alive using non-configuration instance ~ note ViewModelStoreOwner interface LiveData allows observing events (while retaining the last value, and emitting last value on re-subscription) – allowing automatic unsubscription and the ability to receive updates on future changes. Room is an ORM that lets you observe the database directly as LiveData... And more!
  2. For LiveData behavior, see LifecycleBoundObserver
  3. PagedListAdapter uses an AsyncPagedListDiffer to calculate the difference between the loaded pages.
  4. I wanted to show off a positional data source that wraps RealmResults, but it just didn’t fit into the slides. As in, it would not have been informative, imo. You can check it out here, though: https://github.com/Zhuinden/room-live-paged-list-provider-experiment/blob/e76f3d2c5e1be402ff0fe2dc46e62f9e2a102080/app-realm/src/main/java/com/zhuinden/realmlivepagedlistproviderexperiment/util/RealmTiledDataSource.java
  5. LivePagedListBuilder creates a ComputableLiveData which executes the re-query when the underlying data source is invalidated.
  6. PagedListAdapter handles showing items from the paged list using the AsyncPagedListDiffer. public static DiffCallback<User> DIFF_CALLBACK = new DiffCallback<User>() { @Override public boolean areItemsTheSame(@NonNull User oldItem, @NonNull User newItem) { return oldItem.userId == newItem.userId; } @Override public boolean areContentsTheSame(@NonNull User oldItem, @NonNull User newItem) { return oldItem.equals(newItem); } };
  7. In the official sample, this is what PagingRequestHelper is for: ensuring that a network request is called only once at a time.
  8. We might want to specify our own FetchExecutor for the LivePagedListBuilder.