Slide 1

Slide 1 text

Boost the Quality of Your App with Firebase Doug Stevenson @CodingDoug

Slide 2

Slide 2 text

50% Stability & bugs When leaving a 1 star review, 50% of the time the user mentions the app stability and bugs Google analysis on Play reviews, top 10 English speaking countries, last 365 days of reviews @CodingDoug

Slide 3

Slide 3 text

Prevention Find the bugs before your users!

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Firebase Test Lab Test Artifacts • Logcat (crashes) • Videos • Screenshots • Performance
 CPU
 memory
 network ingress/egress @CodingDoug

Slide 7

Slide 7 text

Instrumented Tests • Specific to Android • Focus on the user story / use case • Espresso / UI Automator • Adds extensions to JUnit • Android Testing Support Library: goo.gl/xSxzoj • Testing Codelab: goo.gl/RHdFBY @CodingDoug

Slide 8

Slide 8 text

@RunWith(AndroidJUnit4.class) public class NotesScreenTest { @Rule public ActivityTestRule mNotesActivityTestRule = new ActivityTestRule<>(NotesActivity.class); @Test public void clickAddNoteButton_opensAddNoteUi() throws Exception { // Click on the add note button onView(withId(R.id.fab_add_notes)).perform(click()); // Check if the add note screen is displayed onView(withId(R.id.add_note_title)).check(matches(isDisplayed())); } } @CodingDoug

Slide 9

Slide 9 text

(Local) Unit Tests • Runs fast on your computer • Focus on testing individual classes and methods • Dependency injection / object mocking @CodingDoug

Slide 10

Slide 10 text

Android Instrumented Tests Local Unit Tests @CodingDoug

Slide 11

Slide 11 text

Protip: Run your unit tests along with instrumented tests android {
 sourceSets {
 androidTest {
 java.srcDirs += "src/test/java"
 }
 }
 } testImplementation 'org.mockito:mockito-core:2.17.0'
 androidTestImplememntation 'org.mockito:mockito-android:2.17.0' @CodingDoug

Slide 12

Slide 12 text

Protip: Enable StrictMode when running in Test Lab Turn StrictMode violations into actionable crash reports in Test Lab. @CodingDoug

Slide 13

Slide 13 text

// During Application.onCreate() or ContentProvider.onCreate() private void enableStrictMode() { if (shouldEnableStrictMode()) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyLog() .penaltyDeath() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectAll() .penaltyLog() .penaltyDeath() .build()); } } @CodingDoug

Slide 14

Slide 14 text

private boolean shouldEnableStrictMode() { return BuildConfig.DEBUG; } @CodingDoug

Slide 15

Slide 15 text

private boolean shouldEnableStrictMode() { ContentResolver resolver = context.getContentResolver(); String isRunningInTestLab = Settings.System.getString(resolver, "firebase.test.lab"); return BuildConfig.DEBUG || "true".equals(isRunningInTestLab); } @CodingDoug

Slide 16

Slide 16 text

// https://issuetracker.google.com/issues/36951662 private void workaroundEnableStrictMode() { enableStrictMode(); if (Build.VERSION.SDK_INT >= 16) { // restore strict mode after onCreate() returns. new Handler().postAtFrontOfQueue(new Runnable() { @Override public void run() { enableStrictMode(); } }); } } @CodingDoug

Slide 17

Slide 17 text

But I don’t want to write tests @CodingDoug

Slide 18

Slide 18 text

Espresso Test Recorder • Android Studio
 Menu → Run → ⚫︎Record Espresso Test • Interact with your app • Generates Espresso code to match your actions @CodingDoug

Slide 19

Slide 19 text

@CodingDoug

Slide 20

Slide 20 text

@CodingDoug

Slide 21

Slide 21 text

But I don’t even want to have tests @CodingDoug

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

Robo Test • Only an APK with is required • Automatically logs in with Google Auth • Can pre-fill form fields (e.g. user/pass login, search) • Configure maximum crawl depth and timeout • Algorithm improves over time • Additional test artifact: Activity map • Roboscript @CodingDoug

Slide 24

Slide 24 text

But I’m building a game @CodingDoug

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Game Loop Test (beta) • Sequence of scenarios driven by Android Intents • Performance += FPS • Game Loop Codelab: goo.gl/sn4RNp • “Mechahamster” sample game • Project home: goo.gl/8chQ8p • Test Lab Guide: goo.gl/16xSPn @CodingDoug

Slide 27

Slide 27 text

@CodingDoug

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

How do I run a test? @CodingDoug

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

@CodingDoug

Slide 32

Slide 32 text

gcloud firebase test android run \ --type instrumentation \ --app app-debug-unaligned.apk \ --test app-debug-test-unaligned.apk \ --device model=Nexus6,version=21,orientation=portrait \ --device model=Nexus7,version=19,orientation=landscape @CodingDoug

Slide 33

Slide 33 text

Community Support for testing with CI • Walmart’s “Flank” for sharding tests
 goo.gl/5ApxVU • Circle CI integration
 goo.gl/xoPFqY @CodingDoug

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

Billing for Firebase Test Lab • Spark and Flame payment plans: daily limits • Virtual devices: 10 tests/day • Physical devices: 5 tests/day • Blaze payment plan: per-minute billing • Virtual devices: $1/device/hour • Physical devices: $5/device/hour @CodingDoug

Slide 36

Slide 36 text

@CodingDoug

Slide 37

Slide 37 text

@CodingDoug

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

Crashlytics features • Unity support • NDK support • Search • Webhooks • Cloud Functions trigger • Integration with Google Analytics for Firebase • Integration with Slack (new!) @CodingDoug

Slide 40

Slide 40 text

public interface UncaughtExceptionHandler { /** * Method invoked when the given thread terminates due to the * given uncaught exception. *

Any exception thrown by this method will be ignored by the * Java Virtual Machine. * @param t the thread * @param e the exception */ void uncaughtException(Thread t, Throwable e); } @CodingDoug

Slide 41

Slide 41 text

public class MyHandler implements Thread.UncaughtExceptionHandler { private final Thread.UncaughtExceptionHandler mPriorExceptionHandler; public MyHandler(Thread.UncaughtExceptionHandler prior) { mPriorExceptionHandler = prior; } @Override public void uncaughtException(Thread thread, Throwable throwable) { // Deal with throwable here if (mPriorExceptionHandler != null) { mPriorExceptionHandler.uncaughtException(thread, throwable); } } } @CodingDoug

Slide 42

Slide 42 text

Thread.UncaughtExceptionHandler prior = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler( new MyUncaughtExceptionHandler(prior)); @CodingDoug

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

Performance == Quality @CodingDoug

Slide 45

Slide 45 text

60% Speed, design & usability When leaving a 5 star review, 60% of the time the user mentions the speed, design or usability Google analysis on Play reviews, top 10 English speaking countries, last 365 days of reviews @CodingDoug

Slide 46

Slide 46 text

https://goo.gl/BnWcPK

Slide 47

Slide 47 text

Systrace Advantages • Lots of detail • Custom traces Disadvantages • App runs slow • You typically have to know what you're looking for • Can't collect data from the wild, only connected devices @CodingDoug

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

Firebase Performance Monitoring • Runs in production • Negligible overhead • Automatic and Custom traces • Automatic HTTP/S traffic metrics @CodingDoug

Slide 50

Slide 50 text

Trace • A report for a period of time with a well-defined beginning and end • Nominally has a duration • Also may contain “metrics" for performance-related events during the trace @CodingDoug

Slide 51

Slide 51 text

Automatic Traces - no coding necessary! • App Start (cold) • Screen (each Activity start / stop) • App in foreground • App in background @CodingDoug

Slide 52

Slide 52 text

Automatic Traces - App Start ContentProvider.onCreate() Activity.onResume() But you might want something different! @CodingDoug

Slide 53

Slide 53 text

Manual Traces • Trace trace = FirebasePerformance.startTrace(name); • trace.stop(); • @AddTrace annotation @CodingDoug

Slide 54

Slide 54 text

Metrics • Associate a string/int pair to a Trace • Typically better to measure ratios than absolute values • Abs value if you want to compare similar metrics between traces cache_hit++ cache_miss++ dropped_frames += 10 @CodingDoug

Slide 55

Slide 55 text

Opinionated Performance Dashboard @CodingDoug

Slide 56

Slide 56 text

Android Vitals - get clues for what to measure • In the Google Play Console: goo.gl/rkztG1 • Performance-related stats • Slow rendering (jank) • Frozen frames • Stuck WakeLocks @CodingDoug

Slide 57

Slide 57 text

Automatic HTTP/S transaction metrics • Response time • Payload size • Success rate • URL pattern globbing
 
 yourcdn.com/*.jpg
 api.yourdomain.com/v1/users/*
 api.yourdomain.com/v1/users/*/history/* @CodingDoug

Slide 58

Slide 58 text

HTTP/S transaction metrics breakdown • App version • Device • Country • OS level • Carrier • Radio @CodingDoug

Slide 59

Slide 59 text

Network Performance Summary @CodingDoug

Slide 60

Slide 60 text

How HTTP/S monitoring works Build time bytecode manipulation (with ASM) URL url = new URL("https://firebase.google.com");
 HttpsURLConnection conn = (HttpsURLConnection)
 url.openConnection(); URL url = new URL(“https://firebase.google.com”);
 HttpsURLConnection conn = (HttpsURLConnection)
 FirebasePerfUrlConnection.instrument(url.openConnection()); Decorator inside! @CodingDoug

Slide 61

Slide 61 text

How HTTP/S monitoring works buildscript {
 dependencies { classpath ‘com.google.firebase:firebase-plugins:1.1.5' }
 } apply plugin: 'com.google.firebase.firebase-perf' dependencies {
 compile ‘com.google.firebase:firebase-perf:15.2.0'
 } Uses the Transform API: goo.gl/PwBcLE @CodingDoug

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

Firebase Remote Config • Use it to plan and stage rollouts of new features • Quickly back off the new feature if it has problems @CodingDoug

Slide 64

Slide 64 text

@CodingDoug

Slide 65

Slide 65 text

@CodingDoug

Slide 66

Slide 66 text

@CodingDoug

Slide 67

Slide 67 text

@CodingDoug

Slide 68

Slide 68 text

FirebaseRemoteConfig rc = FirebaseRemoteConfig.getInstance(); if (rc.getBoolean("using_cool_new_feature")) { // do cool new stuff } else { // do the boring old stuff } @CodingDoug

Slide 69

Slide 69 text

Firebase Remote Config • Find out what your users prefer by experimentation • Study the results in Google Analytics for Firebase @CodingDoug

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

Firebase Remote Config (for perf) • Compare CDN/server performance • Compare cache configuration strategies @CodingDoug

Slide 72

Slide 72 text

final FirebaseRemoteConfig rc = FirebaseRemoteConfig.getInstance(); int cache_size = (int) rc.getLong("cache_size"); // init cache with cache_size // later... Trace trace = FirebasePerformance.startTrace("my_trace"); Item item = cache.get("some_resource"); if (item != null) { trace.incrementMetric("cache_hit_size_" + cache_size); } else { trace.incrementMetric(“cache_miss_size_" + cache_size); } trace.stop(); @CodingDoug

Slide 73

Slide 73 text

Resources • Firebase home
 https://firebase.google.com/ • Slack
 https://firebase.community/ • YouTube
 http://youtube.com/firebase • Blog
 http://firebase.googleblog.com/ • Twitter
 https://twitter.com/Firebase

Slide 74

Slide 74 text

Thank you! Confidential + Proprietary Doug Stevenson @CodingDoug youtube.com/firebase firebase.googleblog.com