Skip to content

Instantly share code, notes, and snippets.

@firekesti
Last active October 13, 2017 19:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save firekesti/95ff5791d7a69a77b664c717c84389ae to your computer and use it in GitHub Desktop.
Save firekesti/95ff5791d7a69a77b664c717c84389ae to your computer and use it in GitHub Desktop.
DroidCon NYC 2017 Notes

Doze

Background execution limit in Oreo
Oreo stops background services when the app goes into the background
Also can't start a background service from the background
Users = happy, less battery drain!
No more global CONNECTIVITY_CHANGE broadcast!!!!
This user is VERY happy...
But because of the change in how global broadcasts work, certain custom Intents can stop working
need to use a custom permission to get around this - see slides (TBD)

COOL THING: Java lets you write 30_000 for 30000 to help delineate thousands (or really any additional readability for numbers)

AlarmManager:
[Insert Google's flowchart for setExact]
API < 14, use set()
API 14-22 use setExact()
API 23-26 use setExactAndAllowWhileIdle()
but again, ONLY if it needs to be done at an EXACT time! Like the user requests a reminder, etc.
No one's database update needs to happen exactly every 60 minutes.

JobScheduler:
way nicer API
but! only 21+ and some features only 24+ or 26+!
nice: Job setPersisted automatically reschedules itself after a reboot

JobIntentService
a replacement for IntentService, uses JobScheduler on O+
only for running immediate tasks, not delayed.

Solution: github.com/evernote/android-job

Refactoring

Slides https://www.slideshare.net/godfreynolan/android-refactoring
2k lines in an Activity? EGADS (haha but I've seen that before...)
Subjectivity is what kills junior developers
ball of mud pattern - default for android :(
Why Refactor?
understand code
how reliable? how predictable?
remove technical debt
How to be more proactive - Jenkins is too far-removed
"Metrics Reloaded" Android Studio plugin for code analysis
make sure to have unit tests to easily ensure nothing breaks before/after refactoring
cycle: move, extract, fix, run tests
Martin Fowler book on refactoring
metrics are a guide, not a mandate! too much subjectivity is bad, but don't become obsessed with getting everything to 0.
AS has a great refactoring tool - extract methods/delegates
Slide 51 has a lot of good links

View Performance Deep Dive

anatomy of a scroll
onCreateViewHolder - expensive!
XML, resources, allocations, findViewById
onBindViewHolder - should be cheap, just setters
text is SLOW 🐌
typefaces, dimensions
ex. using systrace to check draw/measure for TextView
how to speed it up? reduce view tree size! (if only...)
ImageView -> ImageDrawable: why?
reduce the tree depth. can use Drawable directly on a parent view
TextView is basically a delegate to android.Text.Layout
Litho (by Facebook): a set of maintained optimizations
but also, all the crap it's hard to do when replacing Views with Drawables:
accessibility, RTL, focus states
if drawable gets invalidated, ALL displaylists must be recreated for parent view's children

The Resurgence of SQLite

with examples all in Kotlin! now I feel behind...
different sorts of storage systems

Flat Files:
key->value pairs, simple.
can also serialize objs to json
can also nest this! but really not good for complex stuff.

Object DBs:
they're ok, but a bit Magic. have to manage threads
query: db.where(user).greater("age", 20) pretty gross!!! string keys, not checked.

ORMs:
like Object DBs but it's living over SQL
but, wind up with same issues as ObjDBs: "stringly-typed", no typechecking.
not a bad concept, but doesn't play nice with Android threads

SQL:
define data, more tables
example: friend checkin, get all friends, then all checkins with friends, but filtering in Java/mem is EXPENSIVE
so: select friend from friendship where friend = me
can also filter the checkin part. WAY FASTER (graphs in slides)
Debugging SQL:
Stetho can look at dbs! also something called "explain query plan" which breaks down complex queries into sql, er, assembly?

Android:
SQLiteDatabase: all setup in strings, pull out keywords, try/catch cursor, no validation, col names, names for computed values.
really Awful. platform team knows this.

Solutions! SqlDelight, and Room. (Room being basically Retrofit for SQL - another presentation on this below).

Advanced Networking with RxJava and Retrofit

beyond the basics, what guides don't cover usually: retries/failues, lifecycle, loading indicators
move your understanding
callbacks -> observable stream (I don't totally feel like I'm here yet)
retryWhen: Response w/ share() = awesome
support library ContentBarLoadingIndicator = no flashes, has a minimum loading time
DON'T: doOnSubscribe(()->loadingIndicator.show()) -> hide
leaks views, UI too tightly coupled
split stream into different subscribers
too advanced :(
ok, Config Changes
RxJava2: disposable (are we still using 1?)
keep track of disposables/subscriptions
or CompositeDisposable.add(disposable).add().add()etc
and cancel everything with one call in onStop!
HOLY SHIT: Activity.onRetainNonConfgurationInstancee(Object). check talk for details?
allows you to keep an object during config change but I missed the advice on what to keep or not

Deep Android Integrations

meh, not even gonna type these up. how Uber allows login from other apps via their app already installed on the device.

Window Fitting

Slides:
https://photos.google.com/share/AF1QipNFHDcQ8dYK_qMxCjPU4vp1mWQZUEYWHvLklKRAmruZ_z5xmQptv8WPsUjEH5_zlw?key=azg3OUFTUFV3d0xwaF9QTUllYUUybzhlcFZEckRR
Window = top-level View
each Activity has one implicitly
also Dialogs (yep, I've had to alter dialog window layout params)
why fit to the window? [example shows nav bar covering media controller's rightmost button]
don't let system overlap activity controls!!!

Inset: how system says where SysUI is or will be
<19: setSystemUiVisibility(), SYSTEM_UI_FLAG_LAYOUT_{STABLE, HIDE_NAV, FULLSCREEN} (these will be called WTFs here on out - "window type flags")
can call on ANY view in hierarchy and it will propagate up!
19: translucent system bars! (kind of a mistake, though). new theme attrs: android:windowTranslucentStatus (auto-sets WTFs)
21: android:windowDrawsSystemBarBackgrounds, android:statusBarColor. but! WTFs are NOT implied, only w/translucent
android:fitSystemWindows="true" adds padding to base window to match SysUI
CoordinatorLayout auto calls the WTFs for ALL children, have fitSystemWindows=true
but now for the Real Way:
WindowInsets (Compat)
takeaways:
DrawerLayout/CoordinatorLayout: apply fitSystemWindows liberally
avoid translucent status bars explicitly
status bar size = NOT A DIMEN, use View.setOnApplyWindowInsetsListener, return consumeWindowInsets

GraphQL

apparently a field with an exclamation point means NON-nullable, not that it could be null. yay
steps to hooking up to the server in apps: model data, get network layer working, transform data, persist data
Apollo handles....all of that?? No gson, retrofit, gson again, database.
a not-quite-OkHttp cache - streams response to cache same time as parsing
normalized cache: more powerful, raw responses
allows multiple queries to share same cached values

No Internet? No Problem!

on every user action, check whether network is available - to know whether to queue for later and tell user
status fields on model object to ensure correct state when syncing to server
store as much data offline as possible. can use Realm, Room, etc.
Separate UI from network!!!
MVP with EventBus, presenter with onAttach/onDetach, sub/unsub to EventBus
Separate Service that runs when application starts
@Subscribe(priority=1) allows service NOT to run the EventBus listener while active! app will consume it first.
but if app doesn't consume, allows service to show a notification to the user when the sync finishes.
Queue multiple network requests
Android Job library (see above!) wraps AlarmManager, JobScheduler, etc.
JobCreator: makes Jobs by a string key
JobManager: schedules jobs by key on a queue if no network
What's next?
SMS fallback: send notifs to user, but also to server!
kotlin - coroutines - async code paths
push notifications

Debugging Without a Stack Trace

this medium article, but as a talk:
https://medium.com/@andreworobator/debugging-without-a-stacktrace-using-android-studios-performance-monitors-a0d601afd814

Vector Assets

PNGs are bad for known reasons (duplication, fwd-compat)
Vecors are good for known reasons (sharp, small, animatable)
SVG is kinda nuts (animated gifs, embedded js??)
so: Android uses a subset of SVG for VectorDrawable
tip: width/height is used for wrap_content measuring. viewportWidth/Height is the space for the path data.
Move to, Line to, Curve to, Arc to, Z to close
group with transforms - scale, rotate
clip-path with masking - works, but aliased :(
path -> can trim path
alpha, but only on root component. otherwise bake into path color
android:tint on
can use ?attrs for stroke, fill, etc
API 24+ = colorstatelists, gradients! (compat??)
ooh! <appt:attr name=fillColor></></> = gradient item color offset :O
radial gradient for drop-shadow replacement
gradient tile mode and hard stops! (is everything I know about android gradients wrong?? or just outdated?)
"candy cane" loading bar - draw off viewport, animate to left, repeat
github/sketch:svgo-compressor: less decimal precision is cheaper to inflate
new: systrace tags on VectorDrawable, are complex vectors taking time to draw?
Android Studio has a svg->VectorDrawable converter, but it's not as good as inloop's yet. but they're working on it...
j.mp/sugo-config, SVGOMG (svg optimizer's missing gui)
I've used this now and it's great!!!
avoid "hand-coding SVGs like an animal"
COMPAT!! app:srcCompat
AppCompatResources.getDrawable
recommended for DataBinding (will this fix KitKat?)
ShapeShifter for ObjectAnimation/path, https://ShapeShifter.design
Tips: prefer animating alpha on root, not path
prefer rotate/scale if possible over path morphing

Elevated UX

Elevation
urgency, immediacy, to show temporary state
Ink
boundaries, overscroll
focus/error/disabled states
Grids
loose grid for shopping, tight grid for gallery/information
two ways to render news items:
in cards: a "rougher surface", more deliberate
as ink: a "smoother surface", more scannable
Type
pairing fonts creates relationship between body/headline
even if text size changes
Motion
digital interfaces that feel natural
physics-based motion can feel faster!
uncanny-valley of motion - not quite like the physical world, throws people off
use motion to direct attention
Why Material?
system builds on what we already know about the world

Building for Android Oreo

this presentation has the most amazing Eclair graphic - find the slides!!!
Oreo adds limits to help choreograph apps sharing the system
K: alarm inexact, M: doze, N: doze lite, fewer broadcasts
O: no starting service in the background, start foreground still fine
location data also throttled in background
users turn off location because it's such a battery drain (yep been there)
but developers (ahem google) still want location! tragedy of the commons. everyone wants it, then nobody gets it.

foreground versus background
foreground: visible app, foreground service (notif), background service bound to foreground app
background: not visible, job service, broadcast receiver
if expensive work is being done, users should know!
foreground services work as expected
only YOU can figure out when it safe to start a service
the intent is for apps to move to Scheduling

Foreground Service: would user approve?
e.g. music, nav, fitness, downloading
don't blindly use a foreground service

Location: limit to "several times an hour". can use batching for more data.
ex. system collects every 10m, app gets every 30m (but all three location points).
wifi scans are efficient, bluetooth even moreso
system throttles location when connected to same SSID (since user isn't moving)
Location Limit Strategies:
use geofencing for larger areas
Starbucks? use Beacons (too damn many in the same "large" or even "block"-level area, geofencing not exact enough
Rethink strategies for pre-O! frequency, latency, accuracy. Do you really need to track location all the time and annoy the user?
PLEASE STOP DOING THIS (a personal note)

ViewModels, LiveData, and Lifecycles, Oh My!

these problems are HARD ON ANDROID: interruptions, rotation (!#@%!% grumbling), async+destroyed UI, OS killing
Why???
Activity handles UI data, processes, lifecycle, rotate clears!
So: Activity should ONLY update UI. ViewModel holds data. move Activity data to VM, and on rotate pull same VM instance
LiveData: holds data, lifecycle aware (not independent?)
also Observable!
make VM extend new android.arch.ViewModel
in Activity, ViewModelProvider.of(this).get(MainActivityViewModel.class);
observe the livedata from viewmodel, and add onChangeListener to update UI when data changes.

What's "Lifecycle Aware"? auto adjust behaviour.
actual Lifecycle object! Observers have lifecycle, no manual cleanup! no leaks when updating UI!

What's a ViewModel under the hood?
A retained fragment - takes care of edge cases while not being a :devil: Fragment!

HOW TO MAKE ACTIVIY DO EVEN LESS? AS LITTLE AS POSSIBLE?!

Song Search Example
MutableLiveData searchTerm - mutable since it can change by user
LiveData<List> results - observable only, UI layer can't change these
Repository pattern, repo.search(term) -> returns LiveData<List>
Repo can be Room, SQLite, etc, even a web service!
new search term? don't recreate LiveData, use a transform instead
Transformations.switchMap - don't dupliate anonymous Observers
results = (searchTerm, newTerm) -> return repo.search
map and switchMap both run on main thread! along with all of LiveData ( :< ? guess it has to since it needs to touch views)
Repo = boundary between UI and Data - can be source of truth
[at this point the speaker went super fast, need to look at slides for the last 10 mins or so]

Get a Room

Room is like Retrofit but for SQlite
Retrofit's interface -> Room's @Dao
Retrofit's base url -> Room's @Database
Room works synchronously, but also Flowable/Single/Maybe (from rx), and LiveData.
Web payloads have parent->child data, for for relational data, may have to make multiple endpoint calls
ORMs: pure OO, but at a cost!
Room: makes you think about relationships
may require multiple DAO calls - to avoid main thread

What are we really modeling?
Retrofit models Data Transfer Objects, then we map into clean POJOs not dependent on server whims
Room entities are not "pure" POJOs, have lots of Room data via annotations
Data types may not map cleanly, even with @Embedded and @TypeTransformer
need similar DTO as Retrofit - map to purer objects

Rule Your Realm

looked into "offline" - SQLite on Android a bit of a nightmare, this was before Room. Realm v popular, let's check it out
Realm is cross-platform - swift, java, c++, js
native-code-based, not SQL, uses models/objets

PROS:
speed, tooling support, gradle plugin
is there a deleteRealmIfMigrationNeeded for Room? just drop tables?
Recipe extends RealmObject: id, name, image_url, List
Ingredient extends RealmObject: id, name
Realm.getDefaultInstance(), pizzas = Realm.where(Recipe).equals("is_pizza", true).findAll();
Tooling: Realm Database Browser

CHALLENGES:
partial updates. network has other DTOs: RecipeOverview, RecipeDetail
inserting details with different data clears overview data!
json -> java (Moshi) -> json (model converter) -> Java (realm)
^ um, gross?
orphan data: SQL has foriegn key -> cascading deletes
but Realm is OO, and it's heavy, so don't delete during normal execution: JobScheduler
Reaper: add boolean to objs to "retain", do a sweep on data after
business logic = all objs to keep = query
@ClientContract for the field names - solves "stringly-typed" issues, keeping track of names to refactor a field. Code Generation!
@UserData - also code generation, a Blue Apron secret (for now), used for marking "retain".
generate code, then unit test it!

CONS:
native code! large, sucks to debug, can ABI split, but can't instant-app this one down.

Migrations: not easy anywhere. don't rename classes willy-nilly. :<
safe migrations: always use strings, not constants (they could change! funny how that works out...)
integration test all the things!!!
Threading:
bg: fetch network, write to realm, pos callback into UI thread
fg: separate realm instance! can cause race condition :o

Splitting the Lyft Driver App: Reduce, Reuse, Recycle

Why split the app?
When Lyft started, thinking passengers and drivers would take turns at each, but that's not the case.
reduce app bloat: less cell data download, startup time, room for new features
speed up feature deployment: fewer driver/passender interaction bugs (hallelujah)
different ship cycles, only break one app at a time (lol)
module ownership: domain experts. give owners power to check code

to split:
focus on small, purposeful changes to avoid sticky ball of dough
duplicate app and turn off Passenger mode - not debloated yet, but could start rollout to Play early.
"there is no catch-all for modularization"
Reduce: modules contain only fine-grained responsibility
Reuse: modules may make sense for both driver/passenger
Recycle: uhh I missed this part

HOLY SHIT THEY HAVE 90+ MODULES

split settings module into shared, driver, passenger settings modules
move passenger classes from core into specific modules
"lyft app module" :o split deep links out from appmodule into each feature module the deeplink would point to

this presenter's gif game is on point

is is still in the same repo?
yes! none of this clusterfsck like uber's 350 separate android repos
how did they move drivers to driver app?
a/b test that would block drivers from continuing in shared app, slow rollout as Driver app became more stable
what's in a module?
each feature. "new sound when user enters car". as fine-grained as possible.
how many android engineers?
about 25, several module owners per
build times with all these modules?
gradle didn't handle it well for a while but it's better now

What a great time!!! Thanks DroidCon NYC 2017!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment