Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Boost the Quality of Your App with Firebase (May 22, 2018 Londroid)

Boost the Quality of Your App with Firebase (May 22, 2018 Londroid)

3acd4fb373289e71fd7ebfb287a75a3b?s=128

Doug Stevenson

May 24, 2018
Tweet

Transcript

  1. Boost the Quality of Your App with Firebase Doug Stevenson

    @CodingDoug
  2. 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
  3. Prevention Find the bugs before your users!

  4. None
  5. None
  6. Firebase Test Lab Test Artifacts • Logcat (crashes) • Videos

    • Screenshots • Performance
 CPU
 memory
 network ingress/egress @CodingDoug
  7. 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
  8. @RunWith(AndroidJUnit4.class) public class NotesScreenTest { @Rule public ActivityTestRule<NotesActivity> 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
  9. (Local) Unit Tests • Runs fast on your computer •

    Focus on testing individual classes and methods • Dependency injection / object mocking @CodingDoug
  10. Android Instrumented Tests Local Unit Tests @CodingDoug

  11. 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
  12. Protip: Enable StrictMode when running in Test Lab Turn StrictMode

    violations into actionable crash reports in Test Lab. @CodingDoug
  13. // 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
  14. private boolean shouldEnableStrictMode() { return BuildConfig.DEBUG; } @CodingDoug

  15. private boolean shouldEnableStrictMode() { ContentResolver resolver = context.getContentResolver(); String isRunningInTestLab

    = Settings.System.getString(resolver, "firebase.test.lab"); return BuildConfig.DEBUG || "true".equals(isRunningInTestLab); } @CodingDoug
  16. // 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
  17. But I don’t want to write tests @CodingDoug

  18. Espresso Test Recorder • Android Studio
 Menu → Run →

    ⚫︎Record Espresso Test • Interact with your app • Generates Espresso code to match your actions @CodingDoug
  19. @CodingDoug

  20. @CodingDoug

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

  22. None
  23. 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
  24. But I’m building a game @CodingDoug

  25. None
  26. 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
  27. @CodingDoug

  28. None
  29. How do I run a test? @CodingDoug

  30. None
  31. @CodingDoug

  32. 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
  33. Community Support for testing with CI • Walmart’s “Flank” for

    sharding tests
 goo.gl/5ApxVU • Circle CI integration
 goo.gl/xoPFqY @CodingDoug
  34. None
  35. 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
  36. @CodingDoug

  37. @CodingDoug

  38. None
  39. Crashlytics features • Unity support • NDK support • Search

    • Webhooks • Cloud Functions trigger • Integration with Google Analytics for Firebase • Integration with Slack (new!) @CodingDoug
  40. public interface UncaughtExceptionHandler { /** * Method invoked when the

    given thread terminates due to the * given uncaught exception. * <p>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
  41. 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
  42. Thread.UncaughtExceptionHandler prior = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler( new MyUncaughtExceptionHandler(prior)); @CodingDoug

  43. None
  44. Performance == Quality @CodingDoug

  45. 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
  46. https://goo.gl/BnWcPK

  47. 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
  48. None
  49. Firebase Performance Monitoring • Runs in production • Negligible overhead

    • Automatic and Custom traces • Automatic HTTP/S traffic metrics @CodingDoug
  50. 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
  51. Automatic Traces - no coding necessary! • App Start (cold)

    • Screen (each Activity start / stop) • App in foreground • App in background @CodingDoug
  52. Automatic Traces - App Start ContentProvider.onCreate() Activity.onResume() But you might

    want something different! @CodingDoug
  53. Manual Traces • Trace trace = FirebasePerformance.startTrace(name); • trace.stop(); •

    @AddTrace annotation @CodingDoug
  54. 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
  55. Opinionated Performance Dashboard @CodingDoug

  56. 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
  57. 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
  58. HTTP/S transaction metrics breakdown • App version • Device •

    Country • OS level • Carrier • Radio @CodingDoug
  59. Network Performance Summary @CodingDoug

  60. 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
  61. 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
  62. None
  63. Firebase Remote Config • Use it to plan and stage

    rollouts of new features • Quickly back off the new feature if it has problems @CodingDoug
  64. @CodingDoug

  65. @CodingDoug

  66. @CodingDoug

  67. @CodingDoug

  68. FirebaseRemoteConfig rc = FirebaseRemoteConfig.getInstance(); if (rc.getBoolean("using_cool_new_feature")) { // do cool

    new stuff } else { // do the boring old stuff } @CodingDoug
  69. Firebase Remote Config • Find out what your users prefer

    by experimentation • Study the results in Google Analytics for Firebase @CodingDoug
  70. None
  71. Firebase Remote Config (for perf) • Compare CDN/server performance •

    Compare cache configuration strategies @CodingDoug
  72. 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
  73. 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
  74. Thank you! Confidential + Proprietary Doug Stevenson @CodingDoug youtube.com/firebase firebase.googleblog.com