Skip to content

Instantly share code, notes, and snippets.

@joshfriend
Last active April 29, 2019 04:19
Show Gist options
  • Save joshfriend/bdc48e921442252171c205ea1d4e52af to your computer and use it in GitHub Desktop.
Save joshfriend/bdc48e921442252171c205ea1d4e52af to your computer and use it in GitHub Desktop.
Chicago Roboto 2019 notes
suspend fun thing() {
    with (dispatchers.io) {}
}

Dispatchers:

  • Main
  • IO (network/disk)
  • Default (CPU bound)

Good suspend functions are "main safe"

they had literally less ram than i have fingers

Structured concurrency

When a suspend function returns, it has completed all work. launch { } coroutine builder

  • What cancels coroutine work
  • Who gets the exceptions

Scopes keep track of coroutines

val scope = CoroutineScope(Dispatchers.Main)  // MainScope()
scope.launch { ... }

ViewModel.viewModelScope coroutine scope for android viewmodels (in alpha)

launch does not automatically start in the caller's scope, coroutineScope {} builder will do that though.

  • creates child scope of the caller's scope

  • Only resumes after children completes

  • scope returns when child work is complete

  • scope is cancelled when children are cancelled

  • scope is notified when children error

scope.async {} runs outside the scope * withTimeout(n) * mutex.withLock {}

RxCoroutines -> Flow

~/Dropbox/RandomlyTyping/...

talks as a giant kotlin file with block comments is cool

operator ad hoc polymorphism πŸ‘€

somehow operator fun SomeClass.iterator() makes the class implement Iterable interface???

don't implement both plus() and plusAssign() because of automatic plus-assign sematics with plus() and var references

use clip masks instead of changing layout bounds, avoids expensive layout invalidations

recyclerView.isLayoutFrozen = true

Israel Comocho: Smoke & Mirrors πŸ‘€

Anything fast enough will look good enough

Collapse animation incorrect Z-ordering (just like it is for fragments)

what is a "Span pool"? πŸ‘€

Bitmap rotation turns on bilinear filtering -> jagged edges, add one pixel transparent border (lol)

Keyboard showing:

DecorView
    LinearLayout
        FrameLayout (the one that gets resized)
            LinearLayout
                ActivityContent (android.R.id.content)

use addOnPreDrawListener for activity.window.decorView.viewTreeObserver to animate aandroid.R.id.content size change

dagger-reflect πŸ‘€ SQLDelight πŸ‘€

R class is now generated bytecode instead of java source gen

navigation-fragment is seperate artifact because google wants to allow use of conductor or other view frameworks

Conditional Navigation

use separate graphs (e.g. a login_graph.xml) global action to navigate to a nested graph from anywhere destination fragment subclass a "SecureFragment":

  • Observe auth status
  • Kick over to login flow graph

YOU CAN POP UP TO A NAV GRAPH DESTINATION OMG

<nav-graph> link in activity registration in manifest expands to intent filters

DO NOT FORGET THIS PART:

// inside activity
override fun onNewIntent(intent: Intent) {
  super.onNewIntent(intent)
  navController.handleDeepLink(intent)
}

Testing Navigation

Fragment scenario build fragment without activity

Custom Destination Types

dialog navigator type 😲

Nav graphs do not live outside the parent activity Graph scoped view model in 2.1.0 alphas

Multi-activity might work well for multi-module * nav controller for in-app navigation * implicitly intents for navigating to features externally * Restrict intents to in app?

Suggested 70/20/10 split for unit/integration/ui tests

androidx.test can run in JVM outside emulator/device (using robolectric automatically where neccesary?)

  • Junit/Junit androidx extensions testImplementation
  • testInstrumentaitonRunner -> Junit android runnner
  • @RunWith(AndroidJuni4) on test class
  • ApplicationProvider.getApplicationContext<App>()
  • Include robolectric :(
  • test options include android resources = true

Core

val scenario = ActivityScenario.launch(SomeActivity::class.java)
scenario.moveToState(Lifecycle.State.RESUMED)
scenario.recreate()  // destroy and resume with savedInstanceState
scenario.close()
@get:Rule
val activityRule = ActivityScenarioRule(SomeActivity::class.java)
val scenario = activityRule.scenario

Fragment Testing

val scenario = FragmentScenario.launch(SomeFragment::class.java, bundle)
scenario.moveToState(Lifecycle.State.RESUMED)
scenario.onFragment { fragment ->
    //
}

Espresso

  • View matchers
    • ViewMatchers.withId
  • View actions
    • ViewActions.click
  • View assertions
    • ViewAssertions.matches

Recyclerview espresso -> contrib package Intents watching -> contrib

Truth

optional assertions lib

Project Nitrogin πŸ‘€

What makes mobile special?

  • Focus
  • Intentionality
  • Community
  1. We are all problem solvers

can we better celebrate a long engineering career?

if somebody is happy with the work they are doing, why would you mess with that

  1. priorities and tradeoffs

Sources of burnout

  • culture
    • long hours
    • lots of pressure
  • internal pressure
    • from yourself
  • repetition
    • doing the same thing too long

SimpleExoplayer handles audio sessions automatically :o

Exoplayer is easy enough to use that it should be the default choice for media playback

JSON license: "The software shall be used for Good, not Evil."

-0.0 == 0.0 // true
-0.0.equals(0.0) // false

NaN operators are not reflexive but methods are

JSOM has no NaN or Infinity, but has -0.0

DO NOT use numbers for ids in json, the are treated as floats, which have a much smaller integer range than a Long

java string hashCode sucks, don't rely on it for anything

NetworkBoundResource

repository pattern for choosing between network/database retrieval

Reactive queries:

  • Use transactions to commit updates atomically

Anything with layout_ prefix belongs on the view, not in the style - because they are affected by the parent

Text color/size/font goes in a textAppearance style - not relevant to all widgets, just textview subclasses - Text appearances can be used in spans

style affect only the widget the style is applied to, theme applies to all children in the heirarchy

use R.attr references

Theme Inheritance

  • parent="OtherTheme"used as the base, then apply overrides
  • name="Theme.OtherTheme" alternate for declaring inheritance form OtherTheme
  • Set theme in manifest on activity, or by setTheme() BEFORE INFLATING VIEW
val themedContext = ContextThemeWrapper(context, R.style.Theme)
LayoutInflater.from(themedContext)
// or
inflater.cloneInContext(themedCtx)

custom views defStyleAttr Naming: * colorPrimary and onColorPrimary

getRunAttemptCount - Can be used to cancel a task if it fails repeatedly since the framework will just keep retrying it forever.

periodic workers do not return a success state, only enqueued, running.

Periodic tasks have a 15 minute minimum interval

  • workers can be assigned 1..N tags
  • query work by tag
  • NEVER USE cancelAllWork(), it might cancel work from dependencies using workmanager
  • one-time work and periodic work share tags
  • check isStopped() before doing work, worker constraints might not be met
  • cancel all work before removing the workmanager library :D, if it ever gets added back, enqueued tasks that were leftover will start causing crashes because they don't exist anymore.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment