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
// 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
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
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
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