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. gitignore # Built application files *.apk *.ap_ # Files for

    the ART/Dalvik VM *.dex # Generated files bin/ gen/ out/ ...
  2. 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.
  3. tools folder Try to create a folder and put all

    those files inside this folder.
  4. flavors Flavors are used to create builds with different set-up.

    In most cases two flavors are enough  —  dev and prod.
  5. flavors Different set-up usually means usage of separate: - applicationId

    - versionCode / versionName - server endpoints - google services keys - ...
  6. 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" } }
  7. keystore A keystore is a binary file that contains one

    or more private keys used to sign your application.
  8. 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.
  9. keystore Issues when using local debug keystore: - expiration date

    of 365 days - installing application from multiple computers require uninstalling first
  10. 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
  11. 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 { ... } }
  12. proguard On Android proguard is used for three things: -

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

    shrink unused code  —  helps you to survive the 64k limit - optimize code & APK
  14. 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
  15. proguard Use different proguard rules for release and debug build:

    - rules-proguard.pro - rules-proguard-debug.pro
  16. 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" } }
  17. 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" } }
  18. 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" } }
  19. 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" } }
  20. 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
  21. 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 - ...
  22. 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()); }
  23. 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()); }
  24. 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()); }
  25. 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.<init>(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:
  26. 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.<init>(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:
  27. defaultConfig { versionCode 1 versionName "1.0" } Version Name &

    Code Developers usually hard code values for android versionName & versionCode.
  28. Version Name & Code Issues with usage of hard coded

    values for android versionName & versionCode. - you never know which commit represent specific version
  29. 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
  30. Version Name & Code v0.1 v0.2 v0.3 It’s a common

    practice to use git tags to indicate release of new version.
  31. 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.
  32. 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.
  33. 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.
  34. 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.
  35. 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
  36. Version Name Using git commit hash “1.0-2-gdca226a” you can easily

    find out from which specific commit build was made.
  37. 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.
  38. Version Code Are we going to create git tag for

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

    every intermediate version? - No, for intermediate builds we can use timestamp of HEAD commit
  40. Version Code 65a9e77 dca226a HEAD commit 1 commit 2 commit

    3 versionCode = 1484407970 > Sat Jan 14 2017 15:32:50 UTC
  41. Groovy way to use Git To work with git we

    are going to use great library called grgit.
  42. 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 ...
  43. 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") }
  44. 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") }
  45. Version Name & Code # build.gradle apply plugin: 'com.android.application' apply

    from: "$project.rootDir/tools/script-git-version.gradle" ...
  46. Version Name & Code # build.gradle productFlavors { dev {

    versionCode gitVersionCodeTime versionName gitVersionName } prod { versionCode gitVersionCode versionName gitVersionName } }
  47. 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.
  48. 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
  49. 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 } }
  50. 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
  51. 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 ...
  52. Lint >./gradlew lint BUILD SUCCESSFUL Ran lint on variant release:

    0 issues found Wrote HTML report to: template/app/build/outputs/lint/lint.html
  53. 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" } }
  54. 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" } }
  55. 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" } }
  56. 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" } }
  57. Findbugs # MainActivity.class ... private void someMethod(int variable) { switch

    (variable) { case 1: System.out.println("1"); case 2: System.out.println("2"); } } ...
  58. 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
  59. 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 ...
  60. 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" } }
  61. 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" } }
  62. 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" } }
  63. 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" } }
  64. 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 } } } } }
  65. 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
  66. 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.
  67. 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/" } ... }
  68. 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")
  69. 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")
  70. 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.
  71. 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.
  72. 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.
  73. Continuous Integration Workflow defines the steps of a Build process.

    You can customize it the way you want to suite your build process.
  74. Continuous Integration # build logs +---+---------------------------------------------------+----------+ | ✓ | [email protected]

    | 4.84 sec | +---+---------------------------------------------------+----------+ | ✓ | assembleProdRelease | 118 sec | +---+---------------------------------------------------+----------+ | x | lintProdRelease (exit code: 1) | 35 sec | +---+---------------------------------------------------+----------+