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

Configuring Android Project

Configuring Android Project

Project template available on GitHub: https://github.com/dmytrodanylyk/template

Dmytro Danylyk

February 23, 2017
Tweet

More Decks by Dmytro Danylyk

Other Decks in Programming

Transcript

  1. Configuring
    Android Project
    Dmytro Danylyk
    Atlassian

    View Slide

  2. gitignore
    To quickly generate .gitignore files I recommend you to use gitignore.io site

    View Slide

  3. gitignore
    1. Type in necessary keywords like — Android, Intellij
    2. Click Generate button

    View Slide

  4. gitignore
    # Built application files
    *.apk
    *.ap_
    # Files for the ART/Dalvik VM
    *.dex
    # Generated files
    bin/
    gen/
    out/
    ...

    View Slide

  5. tools folder
    If you have some 3rd party scripts, rulesets or other files don’t just drop them in root
    directory — it will create a mess.

    View Slide

  6. tools folder
    Try to create a folder and put all those files inside this folder.

    View Slide

  7. flavors
    Flavors are used to create builds with different set-up. In most cases two flavors are
    enough  —  dev and prod.

    View Slide

  8. flavors
    Different set-up usually means usage of separate:
    - applicationId
    - versionCode / versionName
    - server endpoints
    - google services keys
    - ...

    View Slide

  9. flavors
    productFlavors {
    dev {
    signingConfig signingConfigs.debug
    versionCode 1
    versionName "1.0-dev"
    applicationId "com.dd.template.dev"
    }
    prod {
    signingConfig signingConfigs.release
    versionCode gitVersionCode
    versionName gitVersionName
    applicationId "com.dd.template"
    }
    }

    View Slide

  10. keystore
    A keystore is a binary file that contains one or more private keys used to sign your
    application.

    View Slide

  11. keystore
    When running or debugging your project from the IDE, Android Studio automatically
    signs your APK with a debug certificate generated by the Android SDK tools.

    View Slide

  12. keystore
    Issues when using local debug keystore:
    - expiration date of 365 days

    View Slide

  13. keystore
    Issues when using local debug keystore:
    - expiration date of 365 days
    - installing application from multiple computers require uninstalling first

    View Slide

  14. keystore
    Issues when using local debug keystore:
    - expiration date of 365 days
    - installing application from multiple computers require uninstalling first
    - google services require keystore SHA-1 fingerprint

    View Slide

  15. keystore
    Generate debug keystore and commit it to version control system.
    signingConfigs {
    debug {
    keyAlias 'androiddebugkey'
    keyPassword 'android'
    storePassword 'android'
    storeFile file('../keystore/debug.keystore')
    }
    release {
    ...
    }
    }

    View Slide

  16. proguard
    On Android proguard is used for three things:
    - shrink unused code  —  helps you to survive the 64k limit

    View Slide

  17. proguard
    On Android proguard is used for three things:
    - shrink unused code  —  helps you to survive the 64k limit
    - optimize code & APK

    View Slide

  18. proguard
    On Android proguard is used for three things:
    - shrink unused code  —  helps you to survive the 64k limit
    - optimize code & APK
    - obfuscate code  —  makes your APK difficult to reverse engineer

    View Slide

  19. proguard
    Obfuscation and code optimization significantly increase compilation time and makes
    debugging harder.

    View Slide

  20. proguard
    Use different proguard rules for release and debug build:
    - rules-proguard.pro
    - rules-proguard-debug.pro

    View Slide

  21. proguard
    buildTypes {
    release {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'),
    "$project.rootDir/tools/rules-proguard.pro"
    }
    debug {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'),
    "$project.rootDir/tools/rules-proguard-debug.pro"
    }
    }

    View Slide

  22. proguard
    buildTypes {
    release {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'),
    "$project.rootDir/tools/rules-proguard.pro"
    }
    debug {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'),
    "$project.rootDir/tools/rules-proguard-debug.pro"
    }
    }

    View Slide

  23. proguard
    buildTypes {
    release {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'),
    "$project.rootDir/tools/rules-proguard.pro"
    }
    debug {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'),
    "$project.rootDir/tools/rules-proguard-debug.pro"
    }
    }

    View Slide

  24. proguard
    buildTypes {
    release {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'),
    "$project.rootDir/tools/rules-proguard.pro"
    }
    debug {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'),
    "$project.rootDir/tools/rules-proguard-debug.pro"
    }
    }

    View Slide

  25. proguard
    # rules-proguard-debug.pro
    -ignorewarnings
    -dontobfuscate
    -dontoptimize

    View Slide

  26. proguard
    # rules-proguard.pro
    # Remove logs
    -assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
    }
    # Proguard configurations for common Android libraries:
    # https://github.com/krschultz/android-proguard-snippets

    View Slide

  27. Configure proguard on early stages, it will be a lot easier.
    android dev tip

    View Slide

  28. strict mode
    Android StrictMode helps you to detect different kinds of problems:
    - closable object is not closed
    - file reading / network requests performed on main thread
    - uri exposed
    - ...

    View Slide

  29. strict mode
    Turn on StrictMode only for debug build:
    if (BuildConfig.DEBUG) {
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    .detectAll()
    .penaltyLog()
    .build());
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
    .detectAll()
    .penaltyLog()
    .build());
    }

    View Slide

  30. strict mode
    Turn on StrictMode only for debug build:
    if (BuildConfig.DEBUG) {
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    .detectAll()
    .penaltyLog()
    .build());
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
    .detectAll()
    .penaltyLog()
    .build());
    }

    View Slide

  31. strict mode
    Turn on StrictMode only for debug build:
    if (BuildConfig.DEBUG) {
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    .detectAll()
    .penaltyLog()
    .build());
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
    .detectAll()
    .penaltyLog()
    .build());
    }

    View Slide

  32. StrictMode:
    A resource was acquired at attached stack trace but never released.
    See java.io.Closeable for information on avoiding resource leaks.
    java.lang.Throwable: Explicit termination method 'close' not called
    at dalvik.system.CloseGuard.open(CloseGuard.java:184)
    at android.database.CursorWindow.(CursorWindow.java:111)
    at android.database.clearOrCreateWindow(AbstractCursor.java:198)
    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
    at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:197)
    at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:237)
    at com.dd.template.MainActivity.onCreate(MainActivity.java:124)
    strict mode
    Example of log when you forgot to close SQLiteCursor:

    View Slide

  33. StrictMode:
    A resource was acquired at attached stack trace but never released.
    See java.io.Closeable for information on avoiding resource leaks.
    java.lang.Throwable: Explicit termination method 'close' not called
    at dalvik.system.CloseGuard.open(CloseGuard.java:184)
    at android.database.CursorWindow.(CursorWindow.java:111)
    at android.database.clearOrCreateWindow(AbstractCursor.java:198)
    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
    at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:197)
    at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:237)
    at com.dd.template.MainActivity.onCreate(MainActivity.java:124)
    strict mode
    Example of log when you forgot to close SQLiteCursor:

    View Slide

  34. defaultConfig {
    versionCode 1
    versionName "1.0"
    }
    Version Name & Code
    Developers usually hard code values for android versionName & versionCode.

    View Slide

  35. Version Name & Code
    Issues with usage of hard coded values for android versionName & versionCode.
    - you never know which commit represent specific version

    View Slide

  36. Version Name & Code
    Issues with usage of hard coded values for android versionName & versionCode.
    - you never know which commit represent specific version
    - whenever you increment versionCode and change versionName you have to
    modify build.gradle file

    View Slide

  37. Version Name & Code
    v0.1 v0.2 v0.3
    It’s a common practice to use git tags to indicate release of new version.

    View Slide

  38. Version Name
    For versionName we can use git describe command.
    a. The command finds the most recent tag that is reachable from a
    commit.
    b. If the tag points to the commit, then only the tag is shown.
    c. Otherwise, it suffixes the tag name with the number of additional
    commits on top of the tagged object and the abbreviated object name of
    the most recent commit.

    View Slide

  39. Version Name
    For versionName we can use git describe command.
    a. The command finds the most recent tag that is reachable from a
    commit.
    b. If the tag points to the commit, then only the tag is shown.
    c. Otherwise, it suffixes the tag name with the number of additional
    commits on top of the tagged object and the abbreviated object name of
    the most recent commit.

    View Slide

  40. Version Name
    For versionName we can use git describe command.
    a. The command finds the most recent tag that is reachable from a
    commit.
    b. If the tag points to the commit, then only the tag is shown.
    c. Otherwise, it suffixes the tag name with the number of additional
    commits on top of the tagged object and the abbreviated object name of
    the most recent commit.

    View Slide

  41. Version Name
    For versionName we can use git describe command.
    a. The command finds the most recent tag that is reachable from a
    commit.
    b. If the tag points to the commit, then only the tag is shown.
    c. Otherwise, it suffixes the tag name with the number of additional
    commits on top of the tagged object and the abbreviated object name of
    the most recent commit.

    View Slide

  42. Version Name
    1.0 65a9e77 dca226a
    tag commit 1 commit 2
    git describe

    View Slide

  43. Version Name
    > 1.0-2-gdca226a
    1.0 65a9e77
    tag commit 1 commit 2
    git describe
    dca226a

    View Slide

  44. Version Name
    1.0 65a9e77 dca226a
    tag commit 1 commit 2
    1.0-2-gdca226a

    View Slide

  45. Version Name
    1.0-2-gdca226a
    Tag name.
    1.0 65a9e77 dca226a
    tag commit 1 commit 2

    View Slide

  46. Version Name
    1.0-2-gdca226a
    Number of commits made after tagged commit.
    tag commit 1 commit 2
    1.0 65a9e77 dca226a

    View Slide

  47. Version Name
    1.0-2-gdca226a
    Short form of commit hash with “g” prefix (stands for git).
    tag commit 1 commit 2
    1.0 65a9e77 dca226a

    View Slide

  48. Version Name
    Using git commit hash “1.0-2-gdca226a” you can easily find out from which specific
    commit build was made.

    View Slide

  49. Version Name
    65a9e77 dca226a 1.0
    commit 1 commit 2 tag
    git describe

    View Slide

  50. Version Name
    65a9e77 dca226a 1.0
    commit 1 commit 2 tag
    git describe
    > 1.0

    View Slide

  51. Version Code
    For versionCode we can use total number of tags.
    Because every git tag indicate some version, versionCode for next version will be
    always greater than previous.

    View Slide

  52. Version Code
    1.0 2.0 3.0
    tag tag tag
    versionCode = 3

    View Slide

  53. Version Code
    Are we going to create git tag for every intermediate version?
    - No, for intermediate builds we can use timestamp of HEAD commit

    View Slide

  54. Version Code
    Are we going to create git tag for every intermediate version?
    - No, for intermediate builds we can use timestamp of HEAD commit

    View Slide

  55. Version Code
    65a9e77 dca226a HEAD
    commit 1 commit 2 commit 3
    versionCode = 1484407970

    View Slide

  56. Version Code
    65a9e77 dca226a HEAD
    commit 1 commit 2 commit 3
    versionCode = 1484407970
    > Sat Jan 14 2017 15:32:50 UTC

    View Slide

  57. Groovy way to use Git
    To work with git we are going to use great library called grgit.

    View Slide

  58. Groovy way to use Git
    # script-git-version.gradle
    buildscript {
    repositories {
    jcenter()
    }
    dependencies {
    classpath 'org.ajoberstar:grgit:1.5.0'
    }
    }
    import org.ajoberstar.grgit.Grgit
    ...

    View Slide

  59. Groovy way to use Git
    # script-git-version.gradle
    ext {
    git = Grgit.open(currentDir: projectDir)
    gitVersionName = git.describe()
    gitVersionCode = git.tag.list().size()
    gitVersionCodeTime = git.head().time
    }
    task printVersion() {
    println("Version Name: $gitVersionName")
    println("Version Code: $gitVersionCode")
    println("Version Code Time: $gitVersionCodeTime")
    }

    View Slide

  60. Groovy way to use Git
    # script-git-version.gradle
    ext {
    git = Grgit.open(currentDir: projectDir)
    gitVersionName = git.describe()
    gitVersionCode = git.tag.list().size()
    gitVersionCodeTime = git.head().time
    }
    task printVersion() {
    println("Version Name: $gitVersionName")
    println("Version Code: $gitVersionCode")
    println("Version Code Time: $gitVersionCodeTime")
    }

    View Slide

  61. Groovy way to use Git
    >./gradlew printVersion
    Version Name: 1.0-6-gd2bef6b
    Version Code: 1
    Version Code Time: 1486290177

    View Slide

  62. Version Name & Code
    # build.gradle
    apply plugin: 'com.android.application'
    apply from: "$project.rootDir/tools/script-git-version.gradle"
    ...

    View Slide

  63. Version Name & Code
    # build.gradle
    productFlavors {
    dev {
    versionCode gitVersionCodeTime
    versionName gitVersionName
    }
    prod {
    versionCode gitVersionCode
    versionName gitVersionName
    }
    }

    View Slide

  64. Version Name & Code
    Run your project and verify app version.

    View Slide

  65. Static Code Analysis Tools
    Static code analysis tool - is source code analyzer. It finds common programming
    flaws like unused variables, empty catch blocks, unnecessary object creation, and so
    forth.

    View Slide

  66. Static Code Analysis Tools
    Static code analysis tool - is source code analyzer. It finds common programming
    flaws like unused variables, empty catch blocks, unnecessary object creation, and so
    forth.
    - Lint
    - Findbugs
    - PMD

    View Slide

  67. Lint
    # script-lint.gradle
    android {
    lintOptions {
    lintConfig file("$project.rootDir/tools/rules-lint.xml")
    htmlOutput file("$project.buildDir/outputs/lint/lint.html")
    warningsAsErrors true
    xmlReport false
    }
    }

    View Slide

  68. Lint
    # build.gradle
    apply plugin: 'com.android.application'
    apply from: "$project.rootDir/tools/script-lint.gradle"
    ...

    View Slide

  69. Lint
    >./gradlew lint
    Execution failed for task ':app:lint.
    Lint found errors in the project; aborting build.
    Wrote HTML report to: template/app/build/outputs/lint/lint.html

    View Slide

  70. Lint
    # template/app/build/outputs/lint/lint.html
    ...
    Priority: 3 / 10
    Category: Security
    Severity: Error
    Explanation: AllowBackup/FullBackupContent Problems.
    The allowBackup attribute determines if an application's data can be
    backed up and restored. It is documented at
    http://developer.android.com/reference/android/R.attr.html#allowBackup
    ...

    View Slide

  71. Lint
    # tools/rules-lint.xml




    View Slide

  72. Lint
    >./gradlew lint
    BUILD SUCCESSFUL
    Ran lint on variant release: 0 issues found
    Wrote HTML report to: template/app/build/outputs/lint/lint.html

    View Slide

  73. Findbugs
    # script-findbugs.gradle
    apply plugin: 'findbugs'
    task findbugs(type: FindBugs) {
    excludeFilter = file("../tools/rules-findbugs.xml")
    classes = fileTree("$project.buildDir/intermediates/classes/")
    source = fileTree('src/main/java/')
    classpath = files()
    reports {
    xml.enabled = false
    html.enabled = true
    html.destination = "$project.buildDir/outputs/findbugs/findbugs.html"
    }
    }

    View Slide

  74. Findbugs
    # script-findbugs.gradle
    apply plugin: 'findbugs'
    task findbugs(type: FindBugs) {
    excludeFilter = file("../tools/rules-findbugs.xml")
    classes = fileTree("$project.buildDir/intermediates/classes/")
    source = fileTree('src/main/java/')
    classpath = files()
    reports {
    xml.enabled = false
    html.enabled = true
    html.destination = "$project.buildDir/outputs/findbugs/findbugs.html"
    }
    }

    View Slide

  75. Findbugs
    # script-findbugs.gradle
    apply plugin: 'findbugs'
    task findbugs(type: FindBugs) {
    excludeFilter = file("../tools/rules-findbugs.xml")
    classes = fileTree("$project.buildDir/intermediates/classes/")
    source = fileTree('src/main/java/')
    classpath = files()
    reports {
    xml.enabled = false
    html.enabled = true
    html.destination = "$project.buildDir/outputs/findbugs/findbugs.html"
    }
    }

    View Slide

  76. Findbugs
    # script-findbugs.gradle
    apply plugin: 'findbugs'
    task findbugs(type: FindBugs) {
    excludeFilter = file("../tools/rules-findbugs.xml")
    classes = fileTree("$project.buildDir/intermediates/classes/")
    source = fileTree('src/main/java/')
    classpath = files()
    reports {
    xml.enabled = false
    html.enabled = true
    html.destination = "$project.buildDir/outputs/findbugs/findbugs.html"
    }
    }

    View Slide

  77. Findbugs
    # build.gradle
    apply plugin: 'com.android.application'
    apply from: "$project.rootDir/tools/script-findbugs.gradle"
    ...

    View Slide

  78. Findbugs
    # MainActivity.class
    ...
    private void someMethod(int variable) {
    switch (variable) {
    case 1:
    System.out.println("1");
    case 2:
    System.out.println("2");
    }
    }
    ...

    View Slide

  79. Findbugs
    >./gradlew findbugs
    Execution failed for task ':app:findbugs'.
    FindBugs rule violations were found. See the report at:
    template/app/build/outputs/findbugs/findbugs.html

    View Slide

  80. Findbugs
    # template/app/build/outputs/findbugs/findbugs.html
    ...
    SF_SWITCH_NO_DEFAULT Switch statement found in
    MainActivity.someMethod(int) where one case falls through to the next
    case
    SF_SWITCH_FALLTHROUGH Switch statement found in
    MainActivity.someMethod(int) where default case is missing
    ...

    View Slide

  81. Findbugs
    # rules-findbugs.xml




    View Slide

  82. Findbugs
    >./gradlew findbugs
    BUILD SUCCESSFUL

    View Slide

  83. PMD
    # script-pmd.gradle
    apply plugin: 'pmd'
    task pmd(type: Pmd) {
    ruleSetFiles = files("$project.rootDir/tools/rules-pmd.xml")
    source = fileTree('src/main/java/')
    reports {
    xml.enabled = false
    html.enabled = true
    html.destination = "$project.buildDir/outputs/pmd/pmd.html"
    }
    }

    View Slide

  84. PMD
    # script-pmd.gradle
    apply plugin: 'pmd'
    task pmd(type: Pmd) {
    ruleSetFiles = files("$project.rootDir/tools/rules-pmd.xml")
    source = fileTree('src/main/java/')
    reports {
    xml.enabled = false
    html.enabled = true
    html.destination = "$project.buildDir/outputs/pmd/pmd.html"
    }
    }

    View Slide

  85. PMD
    # script-pmd.gradle
    apply plugin: 'pmd'
    task pmd(type: Pmd) {
    ruleSetFiles = files("$project.rootDir/tools/rules-pmd.xml")
    source = fileTree('src/main/java/')
    reports {
    xml.enabled = false
    html.enabled = true
    html.destination = "$project.buildDir/outputs/pmd/pmd.html"
    }
    }

    View Slide

  86. PMD
    # script-pmd.gradle
    apply plugin: 'pmd'
    task pmd(type: Pmd) {
    ruleSetFiles = files("$project.rootDir/tools/rules-pmd.xml")
    source = fileTree('src/main/java/')
    reports {
    xml.enabled = false
    html.enabled = true
    html.destination = "$project.buildDir/outputs/pmd/pmd.html"
    }
    }

    View Slide

  87. PMD
    # build.gradle
    apply plugin: 'com.android.application'
    apply from: "$project.rootDir/tools/script-pmd.gradle"
    ...

    View Slide

  88. PMD
    # MainActivity.class
    ...
    private void someMethod(int a, int b, int c, int d) {
    if (a > b) {
    if (b > c) {
    if (c > d) {
    if (d > a) {
    // some logic
    }
    }
    }
    }
    }

    View Slide

  89. PMD
    >./gradlew pmd
    Execution failed for task ':app:pmd.
    1 PMD rule violations were found. See the report at:
    template/app/build/outputs/pmd/pmd.html

    View Slide

  90. PMD
    # template/app/build/outputs/pmd/pmd.html
    ...
    File
    MainActivity.java
    ...
    Line
    31
    Problem
    Deeply nested if..then statements are hard
    to read

    View Slide

  91. PMD
    # rules-pmd.xml







    View Slide

  92. PMD
    >./gradlew pmd
    BUILD SUCCESSFUL

    View Slide

  93. Code Coverage
    Code coverage basically tests how much of your code is covered under tests.
    So, if you have 90% code coverage than it means there is 10% of code that is not
    covered under tests.

    View Slide

  94. Code Coverage
    # script-java-code-coverage.gradle
    apply plugin: 'jacoco'
    task javaCodeCoverage(type: JacocoReport, dependsOn: 'testDebugUnitTest') {
    reports {
    xml.enabled = false
    html.enabled = true
    html.destination "$project.buildDir/outputs/jacoco/"
    }
    ...
    }

    View Slide

  95. Code Coverage
    # script-java-code-coverage.gradle
    def classFileTree = fileTree(
    dir: "${buildDir}/intermediates/classes/",
    excludes: ['**/R.class', '**/R$*.class'],
    includes: ['**/*Presenter*.class']
    )
    classDirectories = files([classFileTree])
    sourceDirectories = files("${project.projectDir}/android/src/main/java")
    executionData = files("${buildDir}/jacoco/testDevDebugUnitTest.exec")

    View Slide

  96. Code Coverage
    # script-java-code-coverage.gradle
    def classFileTree = fileTree(
    dir: "${buildDir}/intermediates/classes/",
    excludes: ['**/R.class', '**/R$*.class'],
    includes: ['**/*Presenter*.class']
    )
    classDirectories = files([classFileTree])
    sourceDirectories = files("${project.projectDir}/android/src/main/java")
    executionData = files("${buildDir}/jacoco/testDevDebugUnitTest.exec")

    View Slide

  97. Code Coverage
    # build.gradle
    apply plugin: 'com.android.application'
    apply from: "$project.rootDir/tools/script-java-code-coverage.gradle"
    ...

    View Slide

  98. Code Coverage
    >./gradlew javaCodeCoverage
    BUILD SUCCESSFUL

    View Slide

  99. Code Coverage
    # template/app/build/outputs/jacoco/jacoco.html
    ...
    Element
    MainPresenter(MainView)
    onExitClicked()
    ...
    Lines
    3
    2
    Coverage
    100%
    100%

    View Slide

  100. Continuous Integration
    Continuous Integration (CI) is the process of automating the build and testing of code
    every time a team member commits changes to version control.

    View Slide

  101. Continuous Integration
    Continuous Integration (CI) is the process of automating the build and testing of code
    every time a team member commits changes to version control.

    View Slide

  102. GitHub repository setup
    Webhooks allow external services to be notified when certain events happen within
    your repository. (push, pull-request, etc.)
    Protect branches to disable force pushing, prevent branches from being deleted, and
    optionally require status checks before merging.

    View Slide

  103. Continuous Integration
    Build Triggered
    Workflow is Executed
    App Ready

    View Slide

  104. Continuous Integration
    Trigger define when to build what.

    View Slide

  105. Continuous Integration

    View Slide

  106. Continuous Integration
    Workflow defines the steps of a Build process. You can customize it the way you want
    to suite your build process.

    View Slide

  107. Continuous Integration

    View Slide

  108. Continuous Integration

    View Slide

  109. Continuous Integration

    View Slide

  110. Continuous Integration
    # build logs
    +---+---------------------------------------------------+----------+
    | ✓ | [email protected] | 4.84 sec |
    +---+---------------------------------------------------+----------+
    | ✓ | assembleProdRelease | 118 sec |
    +---+---------------------------------------------------+----------+
    | x | lintProdRelease (exit code: 1) | 35 sec |
    +---+---------------------------------------------------+----------+

    View Slide

  111. Continuous Integration

    View Slide

  112. View Slide

  113. View Slide

  114. Resources
    www.github.com/dmytrodanylyk/template
    www.pmd.sourceforge.net/integrations.html
    www.findbugs.sourceforge.net
    www.developer.android.com/studio/write/lint.html
    www.guardsquare.com/en/proguard
    www.bitrise.io
    - sample project
    - pmd official site
    - findbugs official site
    - lint user guide
    - proguard official site
    - continuous integration

    View Slide

  115. @dmytrodanylyk
    slides.com/dmytrodanylyk
    www.dmytrodanylyk.com
    S
    speakerdeck.com/dmytrodanylyk
    S

    View Slide