crash wars - the handling awakens
TRANSCRIPT
Crash Wars - The handling awakens
ŽELJKO PLESAC
Independent design & development agency
100 people in 3 offices
18 Android engineers
ABOUT INFINUM
ANDROID NOWADAYS
24 SDK versions
1,294 device vendors with
24,093 distinct Android devices
BEAUTIFUL APPS BEST POSSIBLE UX
I believe that the details will define the difference between good and really amazing. I prefer amazing.
- JULIUS CESAR
CRASHES HAVE AN ENORMOUS EFFECT ON THE
UX
NOBODY LIKES CRASHES*.
* EXCEPT QA
PROVIDE A BETTER CRASH EXPERIENCEYou should try to minimise your crashes.
Optimise your apps in a way you are able to detect crashes
even before they occur.
HOW TO PROVIDE A BETTER CRASH EXPERIENCE?
STATIC CODE CHECKERS
STATIC CODE CHECKERS
• Lint, PMD, Checkstyle, FindBugs
• ErrorProne
• SonarQube
• Facebook Infer
public class DataUtils {
public boolean testMethod(String test){ return test == null && test.length() == 0; } }
JAVA.NET.URL
@Testpublic void urlEquals() throws Exception { URL url = new URL("https://infinum.co"); URL otherUrl = new URL("https://www.infinum.co"); Assert.assertEquals(url, otherUrl);}
JAVA.NET.URL - JAVADOCS
•Two hosts are considered equivalent if both host names can be resolved into the same IP addresses; else if either host name can't be resolved, the host names must be equal without regard to case; or both host names equal to null.
•Since hosts comparison requires name resolution, this operation is a blocking operation.
BUILD YOUR OWN “STATIC CODE CHECKERS” - USE CUSTOM LINT RULES
• define custom Lint rules that are best suited to your needs
• user/project/team specific
IF YOU’RE USING STATIC CODE CHECKERS, DON’T CHEAT.
Be a perfectionist.
WRITE TESTS.
WRITE TESTS
• black box tests, integration tests, UI tests, unit tests…
• TDD
• lot of tools - Unit 4, Espresso, Robolectric, Mockito…
HOW TO INCREASE YOUR TEST QUALITY?
Enforce strict testing rules.
TESTS STABILITY? Test frameworks have bugs. CI servers also. Android platform also.
HANDLE FAILURES
• all failed tests have to be examined carefully
• if your code didn’t cause them, ignore them but test them
once again when new version of testing platform is available
USE CONTINUOUS INTEGRATION
CONTINOUS INTEGRATION
Automate static code checkers & tests execution
Many available products - Jenkins, Travis, CircleCI…
Clients want their features
done yesterday.
Leads to “we’ll fix the build
after the release” behaviour.
BROKEN BUILDS
HANDLE COMMON ANDROID PROBLEMS - MEMORY LEAKS
MEMORY LEAKS
• they will cause problems and crash your applications
• many great tools for detection
A memory leak detection library for Android and Java,
developed by Square (Pierre-Yves Ricau).
LEAK CANARY
• detects memory leaks in your
application, external libraries,
even Android OS itself
• it will not give you an answer
what the cause of a leak is, just
the information that the leak has
occurred
ANDROID STUDIO MEMORY PROFILERS
• Memory monitor, Heap and Allocation Trackers
• introduced in Android Studio 2.0
• can help you investigate memory leaks
ANDROID STUDIO MEMORY MONITOR
WEAK REFERENCES ARE NOT THE ANSWER TO
EVERYTHING.
UNRESPONSIVE APPS
Android OS is multithreaded - there are other threads beside main UI thread.
ENFORCE RULES
• don’t block the main thread - even regular users will notice
flickering
• get familiar with multithreading components
• use Traceview and dmtracedump
STRICT MODE
• use Strict mode in debug builds - set penalty death • detectDiskReads() • detectDiskWrites()
DETECT ANR’S
• ANRWatchDog (link) • detects Android ANRs (Application Not
Responding) problems • can either crash your application or notify you via
callback
CRASH FAST
CRASH YOU APPLICATIONS AS SOON AS POSSIBLE
• Square’s approach to handling crashes (presentation and
video)
• organise your code in a way that it crashes as soon as
possible
• returning null values is evil
public class Person {
private String name;
private String surname;
public Person(String name, String surname) { this.name = name; this.surname = surname; }
… }
public static String getFullName(Person person) { return person.getName() + person.getSurname(); }
public static String getFullName(Person person) { if(person != null){
return person.getName() + person.getSurname(); }
else{ return null;
} }
public static String getFullName(Person person) { if (person == null) { throw new IllegalStateException("Person cannot be null!”); }
return person.getName() + person.getSurname(); }
LOG AND MEASURE
LOG AND MEASURE YOUR CRASHES
• many great tools (Crashlytics, AppsDynamics, Crittercism,
Firebase)
• analyse your crashes • custom ROMs causing crashes? • cheap, low quality devices? • frequency of crashes?
Disable crash reporting in debug builds.
Add GIT SHA to your crash reports.
Add additional data to your reports - custom keys, user
information.
PRO TIPS
HANDLE EXCEPTIONS
I don’t care about warnings, only errors.
- KING HENRIK VIII.
THE TRY-CATCH BLOCK AND EXCEPTIONS• you should care about your handled exceptions
• they have to be logged and analysed
• should contain useful information
• define custom exceptions
public class ProfilePresenterImpl implements ProfilePresenter{
public void showPersonData() { view.showFullName(PersonUtils.getFullName(person))); view.showBirthday(PersonUtils.getFormattedBirthday(person))); view.hideLoadingDialog();
} }
public class ProfilePresenterImpl implements ProfilePresenter{
public void showPersonData() { String fullName = PersonUtils.getFullName(person));
if(fullName != null){ view.showFullName(PersonUtils.getFullName(person)));
}
view.showBirthday(PersonUtils.getFormattedBirthday(person))); view.hideLoadingDialog();
} }
public class ProfilePresenterImpl implements ProfilePresenter{
public void showPersonData() { try{
String fullName = PersonUtils.getFullName(person)); if(fullName != null){ view.showFullName(PersonUtils.getFullName(person)));
} view.showBirthday(PersonUtils.getFormattedBirthday(person))));}
} catch(Exception e){ e.printStackTrace();
view.showErrorDialog(); }
} }
TIMBER
• Utility on top of Android's default Log class
• by Jake Wharton
• can be configured
CRASH REPORTING TREE
private static class CrashReportingTree extends Timber.Tree { @Override protected void log(int priority, String tag, String message, Throwable t) { if (priority == Log.VERBOSE || priority == Log.DEBUG) { return; } // will write to the crash report but NOT to logcat Crashlytics.log(message); if (t != null) { Crashlytics.logException(t); } }}
CRASH REPORTING TREE
@Overridepublic void onCreate() { super.onCreate(); CrashlyticsCore crashlyticsCore = new CrashlyticsCore.Builder()
.disabled(BuildConfig.DEBUG).build(); Fabric.with(this, new Crashlytics.Builder().core(crashlyticsCore).build());
if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); } else { Timber.plant(new CrashReportingTree()); } }
public class ProfilePresenterImpl implements ProfilePresenter{
public void showPersonData() { try{
String fullName = PersonUtils.getFullName(person)); if(fullName != null){ view.showFullName(PersonUtils.getFullName(person)));
}
view.showBirthday(PersonUtils.getFormattedBirthday(person))));} } catch(Exception e){ Timber.e(e, “Failure in “ + getClass().getSimpleName());
view.showErrorDialog(); }
} }
HIDE CRASHES FROM YOUR USERS
Crashes are just exceptions, which are not handled by your application*.
* IN MOST CASES
APP CRASH HANDLERS
• define custom app crash handler in everyone of your
production builds
• avoid ugly system dialogs
• watch out for app restart loops!
public class AppCrashHandler implements Thread.UncaughtExceptionHandler { private Activity liveActivity; public AppCrashHandler(Application application) { application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { @Override public void onActivityResumed(Activity activity) { liveActivity = activity; } @Override public void onActivityPaused(Activity activity) { liveActivity = null; } }); } @Override public void uncaughtException(Thread thread, Throwable ex) { if(liveActivity != null){ Intent intent = new Intent(getApplicationContext(), MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); liveActivity.finish(); liveActivity.startActivity(intent); } System.exit(0); } }
CUSTOM CRASH HANDLER
APPLICATION CLASS
@Overridepublic void onCreate() { super.onCreate(); Thread.setDefaultUncaughtExceptionHandler(new AppCrashHandler(this)); }
→
EXAMPLE
GOOGLE PLAY FEATURES
UTILISE GOOGLE PLAY TOOLS
• alpha/beta test
• staged rollouts
HARSH TRUTH
THERE IS NO SUCH THING AS 100% CRASH FREE ANDROID
APPLICATION.
DEVICE FRAGMENTATION.
THINGS ARE GOING TO BECOME EVEN MORE
COMPLICATED.
MultiWindow support, Jack compiler, new APIs, deprecated
APIs, new programming languages…
ANDROID IS GETTING NEW FEATURES.
ROUNDUP.
Care.
Minimise.
Hide.
Thank you!
Visit www.infinum.co or find us on social networks:
infinum.co infinumco infinumco infinum
@ZELJKOPLESAC [email protected]