Fast List
Scrolling with
ORMLite
Toronto Android Developers Meetup
2016-04-13
Slide 2
Slide 2 text
Eric Fung
Slide 3
Slide 3 text
INTRODUCTION
1
Slide 4
Slide 4 text
Goal: Responsive UI
List scrolling can be tricky
Learn About
CursorAdapter, ORMLite, Traceview,
Deferred Updates, Section Headings
From Problem to Solution
Walkthrough of how I debugged
Slide 5
Slide 5 text
INTRODUCTION
◦ Shopify helps
entrepreneurs
sell things
◦ App for
merchants to
manage their
store’s orders
and products
Slide 6
Slide 6 text
◦ Displays title,
thumbnail and
inventory
◦ List is sectioned
by first letter of
title
INTRODUCTION
Slide 7
Slide 7 text
PROBLEM
2
Slide 8
Slide 8 text
PROBLEM
◦ Few products in
test stores
◦ Try >1500
◦ Spinner visible
for 20s!
Slide 9
Slide 9 text
PROBLEM
Once loaded,
scrolling was
smooth
Slide 10
Slide 10 text
Adapter
◦ Bridge between
ListView and
data
◦ Renders data
into views
REVIEW
ORM
◦ Technique to
access DB from
OO language
◦ Classes instead
of rows
◦ Updates are
just setter calls
Slide 11
Slide 11 text
EXISTING IMPLEMENTATION
Photo by Sonja Langford
◦ Fetch rows, store
into array
◦ Compute section
headings
◦ ArrayAdapter
◦ S-L-O-W
Slide 12
Slide 12 text
USE A CURSORADAPTER
3
Slide 13
Slide 13 text
USE A CURSORADAPTER
Seems obvious!
◦ Cursor is a DB control that lets
you traverse results
◦ Fits with ListView and Adapter
Why didn’t we use it?
◦ All rows needed to compute
section headings
Slide 14
Slide 14 text
USE A CURSORADAPTER
◦ ORMLite’s latest release (Dec
2013) doesn’t have it
◦ PR submitted mid-2014 based
on https://github.
com/campnic/ormlite-android-
extras
◦ But not released…
◦ Build your own JAR or use
JitPack
Slide 15
Slide 15 text
◦ Load time
negligible
◦ But… content
judders while
scrolling
◦ UI thread can’t
keep up
◦ Section headings
lost
USE A CURSORADAPTER
Slide 16
Slide 16 text
“
The key to a smoothly scrolling
ListView is to keep the application’s
main thread … free from heavy
processing. Ensure you do any disk
access, network access, or SQL
access in a separate thread.
https://developer.android.com/training/improving-
layouts/smooth-scrolling.html
Slide 17
Slide 17 text
USE A CURSORADAPTER
◦ 60fps = <17ms per
frame
◦ Images loaded in
background ✔
◦ Database queries
via CursorLoader
✔
◦ What else is
slowing down the
UI thread?
Photo by Bhavyesh Acharya
Slide 18
Slide 18 text
METHOD PROFILING
4
Slide 19
Slide 19 text
METHOD PROFILING
◦ Android Device Monitor is a
suite of tools for debugging and
analysis:
DDMS, OpenGL Tracer,
HierarchyViewer, Systrace,
Traceview
◦ Not to be confused with Android
Monitor
Slide 20
Slide 20 text
TRACEVIEW
Logs method execution over time and
shows execution data, per-thread
timelines, and call stacks.
Good for tracking down performance
problems in your source code.
Slide 21
Slide 21 text
HOW TO
1. Launch app
2. Start method profiling
3. Interact with app (fling list)
4. Stop profiling
Slide 22
Slide 22 text
TRACEVIEW
More info:
https://developer.android.com/tools/performance/index.html
Slide 23
Slide 23 text
METHOD PROFILING
◦ There was a lot of activity on
the main thread
◦ The profiling pane revealed
these calls to be DB calls
◦ But why?
Slide 24
Slide 24 text
METHOD PROFILING
◦ Inventory
information comes
from a foreign
table!
Photo by Ondrej Supitar
Slide 25
Slide 25 text
◦ Variants are a different version
of a product
◦ Merchants want to know if any
products are low on inventory
DATA MODEL
Slide 26
Slide 26 text
@ForeignCollectionField(eager = false)
private Collection variants;
ORMLITE FOREIGN COLLECTIONS
◦ Modelled in ORMLite through a
ForeignCollection
◦ Define whether fetching model
also fetches its foreign
collections (eager vs. lazy)
Slide 27
Slide 27 text
public int numberOfOutOfStockVariants() {
int count = 0;
for (ProductVariant variant : variants) {
if (variant.isOutOfStock()) {
count++;
}
}
return count;
}
ORMLITE FOREIGN COLLECTIONS
Can you spot the DB access?
Slide 28
Slide 28 text
“
http://ormlite.com/javadoc/ormlite-core/doc-
files/ormlite_2.html#Foreign-Collection
…the collection is considered to be
"lazy" and will iterate over the
database using the Dao.iterator()
only when a method is called on the
collection.
Slide 29
Slide 29 text
public int numberOfOutOfStockVariants() {
int count = 0;
for (ProductVariant variant : variants) {
if (variant.isOutOfStock()) {
count++;
}
}
return count;
}
ORMLITE FOREIGN COLLECTIONS
We’d like to keep this abstraction
Slide 30
Slide 30 text
Don’t display all rows
Paginate at bottom
Return join of tables
Joined result set not supported in ORMLite
Denormalize
Put inventory in product, maintain integrity
ALTERNATIVES
Slide 31
Slide 31 text
UPDATE VIEW IN BACKGROUND
5
Slide 32
Slide 32 text
1. When product is rendered, queue
fetch of variants
2. Track which view needs updating
3. Fetch variants in background
4. When data is available, compute
inventory
5. Update the view
IDEA
Slide 33
Slide 33 text
1. Adapter puts entry into shared
structure, mapping product to view
2. Adapter sends message to worker to
fetch variants
3. Worker processes messages in FIFO
order
4. Fetches variant and computes
inventory
5. Removes entry from data structure,
updates view
IMPLEMENTATION
Slide 34
Slide 34 text
FETCH DATA IN BACKGROUND
Smooth scrolling
at last!
Last thing is to
restore the section
headings
Slide 35
Slide 35 text
◦ Why do we care?
▫ It’s in the designs
▫ It allows you to enable fast scrolling
◦ Built-in AlphabetIndexer was
way too slow ƌ
◦ Various blog posts about
CursorAdapter + SectionIndexer
ƌ
SECTION HEADINGS
Slide 36
Slide 36 text
PRECOMPUTING SECTION INDICES
6
Slide 37
Slide 37 text
Section
A group of list
items with
something in
common, e.g. first
letter of title,
album artist
SECTIONINDEXER
Indices
◦ Given index of
section, return
adapter
position
◦ Given adapter
position, return
section it
belongs to
Slide 38
Slide 38 text
If we knew the count of items in
each group of products that start
with the same letter, we could
easily calculate the section indices
Hint hint!
SECTIONINDEXER
Slide 39
Slide 39 text
SELECT SUBSTR(UPPER(title),1,1) AS
section, COUNT(*)
FROM `products`
GROUP BY section
ORDER BY section
A|125
B|123
C|153
D|69
…
GENERATE SECTION INDICES WITH SQL
Slide 40
Slide 40 text
◦ Executes fast
◦ Pre-compute and pass to
Adapter
◦ Define a wrapping list adapter
on top of CursorAdapter
GENERATING SECTION INDICES WITH SQL
Slide 41
Slide 41 text
SUCCESS AT LAST
Place your screenshot here
Photo by Laney Griner
Slide 42
Slide 42 text
CONCLUSION
7
Slide 43
Slide 43 text
CONCLUSION
◦ Smooth list scrolling is essential
for good UX
◦ Use CursorAdapter with DB data
to back your ListView
◦ Profile your app to find hot
spots
◦ Defer updating views if needed
◦ Sometimes SQL > Java