Slide 1

Slide 1 text

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

Slide 44

Slide 44 text

Thanks! QUESTIONS? @gnufmuffin [email protected] efung

Slide 45

Slide 45 text

CREDITS ◦ Template by SlidesCarnival ◦ Photographs by Unsplash