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

The Android Story: Quality, Performance, Stability. ENG (MBLTdev 2017, Moscow)

The Android Story: Quality, Performance, Stability. ENG (MBLTdev 2017, Moscow)

Overview of tools aimed at increasing the quality of the applications. Static code analysis tools, dev tools, application metrics and environment helpers.

Oleksandr Tolstykh

October 30, 2017
Tweet

More Decks by Oleksandr Tolstykh

Other Decks in Programming

Transcript

  1. Lint Out of the box Android Studio integration Custom rules

    Lint baseline! Android specific 200 different checks Fully configurable https://www.bignerdranch.com/blog/building-custom-lint-checks-in-android/
  2. android { // ... lintOptions { warningsAsErrors false abortOnError true

    // github.com/square/okio/issues/58 disable 'InvalidPackage' // App isn't indexable by Google Search disable 'GoogleAppIndexingWarning' // ... } } android { // ... lintOptions { abortOnError false } } Bad! http://tools.android.com/tips/lint-checks The right way!
  3. Lint, check examples MissingSuperCall & other support annotations CommitTransaction ResourceType

    (@StringRes, @DrawableRes, @ColorRes) NewApi http://tools.android.com/tips/lint-checks … and many others
  4. http://tools.android.com/tech-docs/lint-in-studio-2-3#TOC-Creating-a-Baseline android { lintOptions { baseline file("lint-baseline.xml") // your choice

    of filename/path here } } $ ./gradlew lintDebug ......... Wrote XML report to file:///lint-results-debug.xml Lint found 4 warnings :base:lintDebug
  5. http://tools.android.com/tech-docs/lint-in-studio-2-3#TOC-Creating-a-Baseline android { lintOptions { baseline file("lint-baseline.xml") // your choice

    of filename/path here } } $ ./gradlew lintDebug ......... Wrote XML report to file:///lint-results-debug.xml Lint found 1 warning (3 warnings filtered by baseline lint-baseline.xml) :base:lintDebug
  6. Infer Advanced analysis - inter-procedural Command line or plugin Easy

    to install Java, ObjectiveC, C/C++ http://fbinfer.com/
  7. Infer $ brew install infer http://fbinfer.com/docs/analyzing-apps-or-projects.html $ ./gradlew clean $

    infer run -- ./gradlew assembleDebug ... RESOURCE_LEAK: 17 NULL_DEREFERENCE: 5 CONTEXT_LEAK: 1
  8. proj/src/main/java/some/package/FileManager.java:77: error: NULL_DEREFERENCE object returned by `dir.files()` could be null

    and is dereferenced at line 9 7. private void clearStorageSpace(Dir dir, long requiredSpace) { 8. long spaceCleared = 0; 9. > for (File file : dir.files()) { 10. long fileLength = file.length(); 11. http://fbinfer.com/docs/analyzing-apps-or-projects.html
  9. plugins.apply('findbugs') task findbugs(type: FindBugs) { ignoreFailures = false effort =

    'max' reportLevel = 'high' // Report only high priority problems. classes = files("${project.projectDir}/build/intermediates/classes") source = fileTree('src/main/java') classpath = files() reports { xml.enabled = true html.enabled = false } excludeFilter = rootProject.file('quality/findbugs.xml') }
  10. String result = ""; for (String field : fields) {

    result += field + " "; // <-- issue USE_STRINGBUFFER_CONCATENATION }
  11. String result = ""; for (String field : fields) {

    result += field + " "; // <-- issue USE_STRINGBUFFER_CONCATENATION } StringBuilder resultBuilder = new StringBuilder(); for (String field : fields) { resultBuilder.append(field).append(" "); }
  12. findbugs Bug description examples http://findbugs.sourceforge.net/bugDescriptions.html Comparison of String objects using

    == or != Method invokes toString() method on a String Test for floating point equality Method may fail to close stream Unread fields/methods … and many others
  13. plugins.apply('pmd') pmd { toolVersion = '5.5.3' } task pmd(type: Pmd)

    { ignoreFailures = false rulePriority = 2 source = fileTree('src/main/java') ruleSetFiles = rootProject.files('quality/pmd.xml') } http://pmd.sourceforge.net/pmd-4.3.0/howtomakearuleset.html
  14. PMD GodClass DoNotHardCodeSDCard. Use getExternalStorageDirectory() instead of “/sdcard” AvoidInstantiatingObjectsInLoops AvoidCatchingThrowable,

    AvoidCatchingNPE UncommentedEmptyMethodBody, UncommentedEmptyConstructor … and many others https://pmd.github.io/pmd-5.4.1/pmd-java/rules/index.html
  15. plugins.apply('checkstyle') task checkstyle(type: Checkstyle) { ignoreFailures = false showViolations =

    true source = 'src' include '**/*.java' classpath = files() configFile rootProject.file('quality/checkstyle.xml') }
  16. <module name="Checker"> <module name="FileTabCharacter"/> <!-- checks for tabs --> <module

    name="TreeWalker"> <!-- main check group --> <module name="ConstantName" /> <module name="LocalVariableName" /> <module name="AvoidStarImport" /> <module name="UnusedImports" /> <module name="LineLength"> <property name="max" value="100"/> </module> <module name="MethodLength" /> <!-- many other checks --> </module> <module name="SuppressionCommentFilter"/> <!-- allows exceptions --> </module> http://checkstyle.sourceforge.net/checks.html
  17. plugins.apply('cpd') cpdCheck { reports { text.enabled = true xml.enabled =

    false } minimumTokenCount = 100 source = fileTree('src/main/java') } https://github.com/aaschmid/gradle-cpd-plugin
  18. https://pmd.github.io/pmd-5.4.1/usage/cpd-usage.html $ ./gradlew cpdCheck * What went wrong: Execution failed

    for task ':app:cpdCheck'. > CPD found duplicate code. See the report at file:///some/path/cpdCheck.text BUILD FAILED in 4s
  19. public void setupStrictMode() { if (DEVELOPER_MODE) { // if (BuildConfig.DEBUG)

    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() // .detectAll() for all detectable problems .penaltyLog() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); } }
  20. Copy-paste detector -> more than one dev? Than yes! Infer

    -> try it PMD / findbug -> quality obsessed! Lint -> Always! StrictMode -> preferable! Checkstyle -> more than one dev? Than yes! Kotliners? Detekt -> https://github.com/arturbosch/detekt not enough? -> check SonarQube, SonarJava, ............
  21. CI integration RECIPE: - Use pull requests - Run checks

    on every pull-request - Commit status publisher!
  22. What is the outcome? https://developer.android.com/studio/write/lint.html#snapshot Extra effort for the first

    time. Run all quality checks ONLY before making a commit. Quality tools + CI = MAXIMUM PROFIT! Your team should be ready for it. Better app quality, performance, readability.
  23. Stetho Network Inspection Shared preferences viewer View Hierarchy Database Inspection

    http://facebook.github.io/stetho/ Debug bridge in the Browser. Developed by Facebook.
  24. http://facebook.github.io/stetho/ public class MyApplication extends Application { public void onCreate()

    { super.onCreate(); Stetho.initializeWithDefaults(this); } } // OkHttp initialization new OkHttpClient.Builder() .addNetworkInterceptor(new StethoInterceptor()) .build();
  25. CHUCK An in-app HTTP inspector for Android OkHttp clients https://github.com/jgilfelt/chuck

    OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new ChuckInterceptor(context)) .build();
  26. LeakCanary A memory leak detection library for Android and Java.

    https://github.com/square/leakcanary public class MyApplication extends Application { public void onCreate() { super.onCreate(); LeakCanary.install(this); } }
  27. Timber by Jake Wharton “Every time you log in production,

    a puppy dies.” – Jake Wharton https://github.com/JakeWharton/timber Simple log configuration Shipped with embedded lint rules
  28. private void initTimber() { if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); }

    else { Timber.plant(new CrashlyticsTree()); } } Timber.i("Log your message"); // support formatting Timber.i("Hello, %s %s", firstName, lastName);
  29. Logger https://github.com/orhanobut/logger Extensive & pretty logging. Timber integration. Timber.plant(new Timber.DebugTree()

    { void log(int priority, String tag, String message, Throwable t) { Logger.log(priority, tag, message, t); } });
  30. https://github.com/orhanobut/logger E/PRETTY_LOGGER: ┌───────────────────────────── E/PRETTY_LOGGER: │ Thread: main E/PRETTY_LOGGER: ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ E/PRETTY_LOGGER:

    │ Activity.performCreate (Activity.java:6289) E/PRETTY_LOGGER: │ MapActivity.onCreate (MapActivity.java:183) E/PRETTY_LOGGER: ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ E/PRETTY_LOGGER: │ some error message E/PRETTY_LOGGER: └───────────────────────────── I/PRETTY_LOGGER: ┌───────────────────────────── I/PRETTY_LOGGER: │ just some info message I/PRETTY_LOGGER: └───────────────────────────── D/PRETTY_LOGGER: ┌───────────────────────────── D/PRETTY_LOGGER: │ { D/PRETTY_LOGGER: │ "id": "1278", D/PRETTY_LOGGER: │ "name": "John" D/PRETTY_LOGGER: │ } D/PRETTY_LOGGER: └─────────────────────────────
  31. Extract tools into a separate library, if you have too

    many. Your developer settings should be easily maintainable. Add as many dev tools as your project needs. Summary
  32. Build time Launch time Application size Test coverage (JaCoCo) General

    metrics Always keep track of: https://docs.gradle.org/current/userguide/jacoco_plugin.html
  33. Tiny Dancer A real time frames per second measuring library

    for Android https://github.com/friendlyrobotnyc/TinyDancer Fps of your current window
  34. Hugo Annotation-triggered method call logging for your debug builds. https://github.com/JakeWharton/hugo

    @DebugLog public String getName(String first, String last) { SystemClock.sleep(15); // Don't ever really do this! return first + " " + last; } V/Example: ⇢ getName(first="Jake", last="Wharton") V/Example: ⇠ getName [16ms] = "Jake Wharton"
  35. ANDROID DEV METRICS Performance metrics library for Android development. https://github.com/frogermcs/AndroidDevMetrics

    Measures activity lifecycle callbacks Measures Dagger 2 object initializations Schedule method tracing Counts fps drops
  36. JSON to POJO http://www.jsonschema2pojo.org/ { "userId": 12345, "email": "[email protected]", "vehicles":

    [ { "vehicleId": "54321", "plate": "1ABC23" } ], "paymentDetails":null } public class Example { @SerializedName("userId") @Expose private int userId; @SerializedName("email") @Expose private String email; @SerializedName("vehicles") @Expose private List<Vehicle> vehicles = null; @SerializedName("paymentDetails") @Expose private Object paymentDetails; }
  37. Single storage for all platforms Operate with ids, not values

    Automate strings import (if possible) Work with generated files Resources management Strings (translations) or colors management https://crowdin.com/ https://localise.biz/
  38. Add only tools you will really need. AFFECTS BUILD TIME!

    No warnings != no bugs. All given above tools are designed to help, but not annoy you. Wrap up Be patient! It may take longer for the first time...