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

The definitive guide to Android library development

Jeroen Mols
October 20, 2021

The definitive guide to Android library development

Video: https://www.droidcon.com/2021/11/10/the-definitive-guide-to-android-library-development/

Android library/SDK development unfortunately is harder than it should be. This is caused by inferior tooling (Why do you need fat AAR?), missing documentation (What causes transitive dependency conflicts?), and different architecture considerations (Should you modularize your SDK?).

Having switched to full-time Android SDK development two years ago, I'm summarizing my lessons learned into a talk I wish would have existed when I got started.

⚠️ This talk is aimed at anyone curious to level up their knowledge of the Android ecosystem, not just at beginning/seasoned SDK developers!

---------------------------

Further reads:
- Maintaining compatibility in Kotlin libraries by Márton Braun
- The hidden Kotlin gem: Deprecations with ReplaceWith by Marc Reichelt
- Publishing libraries to Maven Central in 2021 by Marton Braun
- Java interoperability policy for major version updates by Jake Wharton
- Public API challenges in Kotlin by Jake Wharton
- How to publish and distribute your Android library by Marco Gomiero
- What is a diamond dependency conflict by Google
- All about Opt-in annotationsby Márton Braun

Jeroen Mols

October 20, 2021
Tweet

More Decks by Jeroen Mols

Other Decks in Programming

Transcript

  1. @MOLSJEROEN
    THE DEFINITIVE GUIDE
    TO ANDROID LIBRARY
    DEVELOPMENT

    View Slide

  2. @MOLSJEROEN

    View Slide

  3. @MOLSJEROEN
    GOAL


    OUR FIRST LIBRARY


    TRANSITIVE DEPENDENCIES


    MODULARIZATION


    DEPENDENCY CONFLICTS


    TIPS & TOOLS

    View Slide

  4. @MOLSJEROEN
    TERMINOLOGY
    • Software Development Kit (SDK)


    • Android library

    View Slide

  5. GOAL

    View Slide

  6. @MOLSJEROEN
    ANDROID LIBRARIES
    APP

    View Slide

  7. @MOLSJEROEN
    ANDROID LIBRARIES
    APP
    COMPOSE OKHTTP …

    View Slide

  8. @MOLSJEROEN
    ANDROID LIBRARIES
    APP
    COMPOSE OKHTTP …
    MAVEN

    View Slide

  9. @MOLSJEROEN
    ANDROID LIBRARIES
    COMPOSE OKHTTP …
    MAVEN
    APP
    MODULE 1 MODULE 2

    View Slide

  10. @MOLSJEROEN
    ANDROID LIBRARIES
    COMPOSE OKHTTP LIBRARY
    MAVEN
    APP
    MODULE 1 MODULE 2

    View Slide

  11. @MOLSJEROEN
    ANDROID LIBRARIES
    dependencies {
    implementation "com.jeroenmols:library:1.0.0"
    }

    View Slide

  12. OUR FIRST LIBRARY

    View Slide

  13. ANDROID STUDIO TO THE
    RESCUE!

    View Slide

  14. @MOLSJEROEN

    View Slide

  15. TOOLING FOR ANDROID
    LIBRARIES IS LACKING

    View Slide

  16. @MOLSJEROEN
    ANDROID STUDIO PROJECT
    PROJECT APP

    View Slide

  17. @MOLSJEROEN

    View Slide

  18. @MOLSJEROEN
    ANDROID STUDIO PROJECT
    LIBRARY
    APP
    PROJECT

    View Slide

  19. @MOLSJEROEN

    View Slide

  20. @MOLSJEROEN

    View Slide

  21. @MOLSJEROEN

    View Slide

  22. @MOLSJEROEN

    View Slide

  23. @MOLSJEROEN
    RUN & TEST
    $./gradlew :app:installDebu
    g

    $ ./gradlew :library:testDebugUnitTes
    t

    $ ./gradlew :library:connectedAndroidTest

    View Slide

  24. @MOLSJEROEN
    BUILD
    $ ./gradlew :library:assembleRelease

    View Slide

  25. @MOLSJEROEN

    View Slide

  26. @MOLSJEROEN
    APK


    •Compiled code


    •Processed resources


    •Android manifest


    •Compiled transitive dependencies


    •Can be run on Android device


    •Signed
    JAR


    •Compiled code


    •Cannot be run on Android device


    •Unsigned

    View Slide

  27. @MOLSJEROEN
    APK


    •Compiled code


    •Processed resources


    •Android manifest


    •Compiled transitive dependencies


    •Can be run on Android device


    •Signed
    AAR


    •Compiled code


    •Processed resources


    •Android manifest


    •Cannot be run on Android device


    •Unsigned

    View Slide

  28. @MOLSJEROEN
    DISTRIBUTE - STEPS
    1.Build a release (see before)


    2.Generate and publish PGP key


    3.Sign build


    4.Upload to Maven central


    => Tutorial by Márton Braun:

    https://getstream.io/blog/publishing-libraries-to-mavencentral-2021/

    View Slide

  29. TOOLING FOR ANDROID
    LIBRARIES IS LACKING

    View Slide

  30. TRANSITIVE DEPENDENCIES

    View Slide

  31. @MOLSJEROEN
    EXTERNAL DEPENDENCIES
    LIBRARY
    APP
    PROJECT
    MAVEN OKHTTP

    View Slide

  32. @MOLSJEROEN
    EXTERNAL DEPENDENCIES
    PROJECT
    MAVEN
    CUSTOMER APP
    LIBRARY

    View Slide

  33. @MOLSJEROEN
    EXTERNAL DEPENDENCIES
    PROJECT
    MAVEN OKHTTP
    CUSTOMER APP
    LIBRARY

    View Slide

  34. @MOLSJEROEN
    EXTERNAL DEPENDENCIES
    dependencies {
    implementation "com.jeroenmols:library:1.0.0"
    implementation "com.squareup.okhttp3:okhttp:4.9.2"
    }

    View Slide

  35. @MOLSJEROEN
    TRANSITIVE DEPENDENCIES
    dependencies {
    implementation "com.jeroenmols:library:1.0.0"
    }

    View Slide

  36. @MOLSJEROEN
    EXTERNAL DEPENDENCIES
    PROJECT
    MAVEN
    OKHTTP
    CUSTOMER APP
    LIBRARY

    View Slide

  37. @MOLSJEROEN
    POM - XML FILE

    com.jeroenmols
    library
    1.0.0


    com.squareup.okhttp3
    okhttp3
    4.9.0



    View Slide

  38. @MOLSJEROEN
    POM - XML FILE
    >

    com.jeroenmols
    library
    1.0.0
    >

    >

    com.squareup.okhttp3>

    okhttp3>

    4.9.0>

    >

    >


    View Slide

  39. @MOLSJEROEN
    POM - XML FILE
    >

    com.jeroenmols>

    library>

    1.0.0>



    com.squareup.okhttp3
    okhttp3
    4.9.0

    >


    View Slide

  40. @MOLSJEROEN

    View Slide

  41. ALWAYS TEST LIBRARIES
    THROUGH MAVEN

    View Slide

  42. MODULARIZATION

    View Slide

  43. @MOLSJEROEN
    APP MODULARISATION
    APP
    FEATURE 2
    FEATURE 3
    FEATURE 4
    FEATURE 5
    FEATURE 6
    CORE 4 CORE 4
    CORE 2 CORE 3
    FEATURE 1
    CORE 1

    View Slide

  44. @MOLSJEROEN
    LIBRARY MODULARIZATION
    LIBRARY
    APP
    PROJECT
    DATABASE UI-COMPONENTS

    View Slide

  45. @MOLSJEROEN

    View Slide

  46. @MOLSJEROEN

    View Slide

  47. @MOLSJEROEN

    View Slide

  48. @MOLSJEROEN
    TEST SETUP
    // app
    Library().initialize()
    // library
    class Library {
    fun initialize() = Database().initialize()
    }

    // database
    class Database {
    fun initialize() = System.out.println(“database ready")
    }

    View Slide

  49. PROBLEM #1

    SUBMODULES NOT INCLUDED IN AAR

    View Slide

  50. @MOLSJEROEN
    LIBRARY SUBMODULE TEST
    ./gradlew :library:assembleRelease

    View Slide

  51. @MOLSJEROEN
    LIBRARY SUBMODULE TEST
    ./gradlew :library:assembleReleas
    e

    // App build.gradl
    e

    dependencies {
    // implementation project(':library')
    implementation
    fi
    les('../library/build/outputs/aar/library-release.aar')
    ...
    }

    View Slide

  52. @MOLSJEROEN

    View Slide

  53. @MOLSJEROEN

    View Slide

  54. TOOLING FOR ANDROID
    LIBRARIES IS LACKING

    View Slide

  55. @MOLSJEROEN

    View Slide

  56. @MOLSJEROEN
    SUBMODULES NOT INCLUDED IN AAR
    1. Release submodules to Maven


    2. Fat AAR


    3. Single module SDK

    View Slide

  57. @MOLSJEROEN
    1. RELEASE SUBMODULES TO MAVEN
    COMPOSE LIBRARY
    MAVEN
    APP
    UI-COMPONENTS DATABASE

    View Slide

  58. @MOLSJEROEN
    1. RELEASE SUBMODULES TO MAVEN
    // Library build.gradl
    e

    dependencies {
    implementation "com.jeroenmols:database:1.0.0"
    implementation "com.jeroenmols:ui-components:1.0.0"
    }

    View Slide

  59. @MOLSJEROEN
    2. FAT AAR
    apply plugin: 'com.kezong.fat-aar'
    ...
    dependencies {
    embed project(path: ':modules:database', con
    fi
    guration:'default')
    embed project(path: ':modules:ui-components', con
    fi
    guration:'default')
    }

    View Slide

  60. @MOLSJEROEN
    3. SINGLE MODULE SDK
    LIBRARY
    APP
    PROJECT

    View Slide

  61. PROBLEM #2

    PUBLIC INTERFACE SUBMODULES EXPOSED

    View Slide

  62. @MOLSJEROEN
    KOTLIN VISIBILITY MODIFIERS
    • private: visible inside this class only


    • protected: same as private + visible in subclasses too


    • internal: visible to all classes inside this module


    • public: visible to all classes

    View Slide

  63. @MOLSJEROEN
    1

    View Slide

  64. @MOLSJEROEN
    PUBLIC INTERFACE SUBMODULES EXPOSED
    1. Internal package


    2. Obfuscate non-public classes


    3. Single module SDK

    View Slide

  65. @MOLSJEROEN
    1. INTERNAL PACKAGE
    package com.jeroenmols.internal.database

    View Slide

  66. @MOLSJEROEN
    2. OBFUSCATE NON-PUBLIC CLASSES
    package a.a.a

    View Slide

  67. @MOLSJEROEN
    2. OBFUSCATE NON-PUBLIC CLASSES
    // proguard-rules.pr
    o

    repackageclasses com.jeroenmols.internal



    package com.jeroenmols.internal.a

    View Slide

  68. @MOLSJEROEN
    3. SINGLE MODULE SDK
    LIBRARY
    APP
    PROJECT

    View Slide

  69. RECOMMENDATION


    LIBRARY MODULARIZATION

    View Slide

  70. @MOLSJEROEN
    MODULARIZATION RECOMMENDATION
    • Modularizing SDKs is tedious


    • Single module for small and mid sized SDKs


    • Spin off reusable maven artefacts for larger SDKs


    • Reduce public API surface


    • Tooling is lacking

    View Slide

  71. TRANSITIVE DEPENDENCY
    CONFLICTS

    View Slide

  72. @MOLSJEROEN
    TRANSITIVE DEPENDENCY CONFLICTS
    PROJECT
    MAVEN
    TRANSITIVE DEPENDENCY 1
    CUSTOMER APP
    ANOTHER LIBRARY

    View Slide

  73. @MOLSJEROEN
    TRANSITIVE DEPENDENCY CONFLICTS
    PROJECT
    MAVEN
    TRANSITIVE DEPENDENCY 1
    CUSTOMER APP
    ANOTHER LIBRARY LIBRARY
    TRANSITIVE
    DEPENDENCY 2
    TRANSITIVE
    DEPENDENCY 3

    View Slide

  74. @MOLSJEROEN
    TRANSITIVE DEPENDENCY CONFLICTS
    1. con
    fl
    icting transitive dependency versions


    2. incompatible transitive dependencies

    View Slide

  75. PROBLEM #1


    TRANSITIVE DEPENDENCY CONFLICTS

    View Slide

  76. @MOLSJEROEN
    CONFLICTING DEPENDENCY VERSIONS
    dependencies {
    implementation "com.jeroenmols:library:1.0.0"
    implementation "com.example:anotherlibrary:2.0.0"
    }

    View Slide

  77. @MOLSJEROEN
    TRANSITIVE DEPENDENCY CONFLICTS
    PROJECT
    MAVEN
    OKHTTP V3
    CUSTOMER APP
    ANOTHER LIBRARY LIBRARY
    OKHTTP V4

    View Slide

  78. @MOLSJEROEN
    TRANSITIVE DEPENDENCY CONFLICTS
    PROJECT
    MAVEN
    OKHTTP V3
    CUSTOMER APP
    ANOTHER LIBRARY LIBRARY
    OKHTTP V4

    View Slide

  79. @MOLSJEROEN

    View Slide

  80. @MOLSJEROEN
    CONFLICT RESOLUTION
    1.Force dependency resolution in CustomerApp


    2.Loosen dependency requirements in library


    3.Remove transitive dependency from library

    View Slide

  81. @MOLSJEROEN
    1. FORCE DEPENDENCY IN CUSTOMERAPP
    implementation('com.jeroenmols:library:1.0.0') {
    exclude group: 'com.squareup.okhttp3', module: 'okhttp'
    }

    View Slide

  82. @MOLSJEROEN
    1. FORCE DEPENDENCY IN CUSTOMERAPP
    con
    fi
    gurations.all
    {

    resolutionStrategy {
    force 'com.squareup.okhttp3:okhttp:3.12.0'
    }
    }

    View Slide

  83. @MOLSJEROEN
    1. FORCE DEPENDENCY IN CUSTOMERAPP
    • No library update required


    • Force untested combinations of dependencies


    • Burden for SDK customers

    View Slide

  84. @MOLSJEROEN
    CASE STUDY: BREAKING API CHANGES
    PROJECT
    MAVEN
    OKHTTP V2
    CUSTOMER APP
    ANOTHER LIBRARY LIBRARY
    OKHTTP V3

    View Slide

  85. @MOLSJEROEN
    OkHtttp v2


    OkHttp v3

    View Slide

  86. @MOLSJEROEN
    CASE STUDY: BREAKING API CHANGES
    package com.square.okhttp
    3

    implementation ‘com.squareup.okhttp3:okhttp3:4.9.0'

    View Slide

  87. PROBLEM #2


    INCOMPATIBLE TRANSITIVE DEPENDENCIES

    View Slide

  88. @MOLSJEROEN
    INCOMPATIBLE TRANSITIVE DEPENDENCIES
    PROJECT
    MAVEN
    PROTOBUF-JAVA
    CUSTOMER APP
    ANOTHER LIBRARY LIBRARY
    PROTOBUF-JAVALITE

    View Slide

  89. @MOLSJEROEN
    INCOMPATIBLE TRANSITIVE DEPENDENCIES
    PROJECT
    MAVEN
    PROTOBUF-JAVA
    CUSTOMER APP
    ANOTHER LIBRARY LIBRARY
    PROTOBUF-JAVALITE

    View Slide

  90. @MOLSJEROEN
    INCOMPATIBLE TRANSITIVE DEPENDENCIES
    $ ./gradlew clean assembleDebug


    > Task :app:checkDebugDuplicateClasses FAILED


    FAILURE: Build failed with an exception.


    * What went wrong:


    Execution failed for task ':myproject:checkReleaseDuplicateClasses'.


    > 1 exception was raised by workers:


    java.lang.RuntimeException: Duplicate class com.google.protobuf.AbstractMessageLite found in modules protobuf-java-3.11.1.jar
    (com.google.protobuf:protobuf-java:3.11.1) and protobuf-javalite-3.11.0.jar (com.google.protobuf:protobuf-javalite:3.11.0)


    Duplicate class com.google.protobuf.AbstractMessageLite$Builder found in modules protobuf-java-3.11.1.jar
    (com.google.protobuf:protobuf-java:3.11.1) and protobuf-javalite-3.11.0.jar (com.google.protobuf:protobuf-javalite:3.11.0)


    Duplicate class com.google.protobuf.AbstractMessageLite$Builder$LimitedInputStream found in modules protobuf-java-3.11.1.jar
    (com.google.protobuf:protobuf-java:3.11.1) and protobuf-javalite-3.11.0.jar (com.google.protobuf:protobuf-javalite:3.11.0)


    ...


    View Slide

  91. @MOLSJEROEN
    INCOMPATIBLE TRANSITIVE DEPENDENCIES
    +--- com.google.
    fi
    rebase:
    fi
    rebase-perf:19.0.7
    | +--- com.google.
    fi
    rebase:
    fi
    rebase-con
    fi
    g:19.0.4
    | | +--- com.google.
    fi
    rebase:
    fi
    rebase-abt:19.0.0
    | | | \--- com.google.protobuf:protobuf-lite:3.0.1

    View Slide

  92. @MOLSJEROEN
    INCOMPATIBILITY RESOLUTION
    1.Substitute dependency in CustomerApp


    2.Remove dependency from transitive dependency of library


    3.Remove transitive dependency from library

    View Slide

  93. @MOLSJEROEN
    2. REMOVE FROM TRANSITIVE DEPENDENCY
    PROJECT
    MAVEN
    PROTOBUF-JAVALITE
    CUSTOMER APP
    ANOTHER LIBRARY LIBRARY
    PBANDK
    PROTOBUF-JAVA

    View Slide

  94. @MOLSJEROEN
    2. REMOVE FROM TRANSITIVE DEPENDENCY
    PROJECT
    MAVEN
    PROTOBUF-JAVALITE
    CUSTOMER APP
    ANOTHER LIBRARY LIBRARY
    PBANDK
    PROTOBUF-JAVA

    View Slide

  95. @MOLSJEROEN
    2. REMOVE FROM TRANSITIVE DEPENDENCY
    ..
    .


    pro.streem.pbandk>

    pbandk-runtime-jvm
    0.9.0


    com.google.protobuf
    protobuf-java



    View Slide

  96. @MOLSJEROEN
    2. REMOVE FROM TRANSITIVE DEPENDENCY
    ..
    .

    >

    pro.streem.pbandk>

    pbandk-runtime-jvm>

    0.9.0>



    com.google.protobuf
    protobuf-java

    >


    View Slide

  97. @MOLSJEROEN
    2. REMOVE FROM TRANSITIVE DEPENDENCY
    pom.withXml {
    def dependencies = asNode().appendNode('dependencies')
    con
    fi
    gurations.getByName(“releaseCompileClasspath").getResolvedCon
    fi
    guration(
    )

    .getFirstLevelModuleDependencies().each {
    ...
    def dependency = dependencies.appendNode(‘dependency’
    )

    if (it.moduleName.contains("pbandk")) {
    def exclusions = dependency.appendNode(‘exclusions'
    )

    def protobufExclusion = exclusions.appendNode('exclusion')
    protobufExclusion.appendNode('groupId', "com.google.protobuf")
    protobufExclusion.appendNode('artifactId', "protobuf-java")
    } } }

    View Slide

  98. @MOLSJEROEN
    2. REMOVE FROM TRANSITIVE DEPENDENCY
    pom.withXml
    {

    def dependencies = asNode().appendNode('dependencies'
    )

    con
    fi
    gurations.getByName(“releaseCompileClasspath").getResolvedCon
    fi
    guration(
    )

    .getFirstLevelModuleDependencies().each
    {

    ..
    .

    def dependency = dependencies.appendNode(‘dependency’
    )

    if (it.moduleName.contains("pbandk"))
    {

    def exclusions = dependency.appendNode(‘exclusions'
    )

    def protobufExclusion = exclusions.appendNode('exclusion')
    protobufExclusion.appendNode('groupId', "com.google.protobuf")
    protobufExclusion.appendNode('artifactId', "protobuf-java")
    } } }

    View Slide

  99. @MOLSJEROEN
    2. REMOVE FROM TRANSITIVE DEPENDENCY
    PROJECT
    MAVEN
    PROTOBUF-JAVALITE
    CUSTOMER APP
    ANOTHER LIBRARY LIBRARY
    PBANDK

    View Slide

  100. @MOLSJEROEN
    2. REMOVE FROM TRANSITIVE DEPENDENCY
    PROJECT
    MAVEN
    CUSTOMER APP
    LIBRARY
    PBANDK

    View Slide

  101. @MOLSJEROEN
    2. REMOVE FROM TRANSITIVE DEPENDENCY
    PROJECT
    MAVEN
    PROTOBUF-JAVALITE
    CUSTOMER APP
    ANOTHER LIBRARY LIBRARY
    PBANDK PROTOBUF-
    JAVALITE

    View Slide

  102. RECOMMENDATION


    TRANSITIVE DEPENDENCIES

    View Slide

  103. @MOLSJEROEN
    TRANSITIVE DEPENDENCY RECOMMENDATION
    1. Minimize transitive dependencies


    2. Use stable versions


    3. Use versions without known vulnerabilities


    4. Don’t use latest dependency versions


    5. Prefer exact versions over ranges


    6. Only use libraries that handle breaking changes


    => Put exceptions in release notes

    View Slide

  104. @MOLSJEROEN
    INVESTIGATE DEPENDENCY CONFLICTS
    ./gradlew :library:dependencie
    s

    $ ./gradlew --console plain :app:dependencies --con
    fi
    guration releaseRuntimeClasspath


    > Task :app:dependencies


    ------------------------------------------------------------


    Project :app


    ------------------------------------------------------------


    releaseRuntimeClasspath - Runtime classpath of compilation 'release' (target (androidJvm)).


    +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.72


    | +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72


    | \--- org.jetbrains:annotations:13.0


    +--- androidx.core:core-ktx:1.3.2


    | +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.71 -> 1.3.72 (*)


    | +--- androidx.annotation:annotation:1.1.0


    | \--- androidx.core:core:1.3.2


    | \--- ...


    +--- androidx.appcompat:appcompat:1.2.0


    | +--- androidx.annotation:annotation:1.1.0




    View Slide

  105. LAST SDK INTEGRATED WILL
    GET THE BLAME

    View Slide

  106. TIPS & TOOLS

    View Slide

  107. @MOLSJEROEN
    2. LIBRARY RESOURCES

    View Slide

  108. @MOLSJEROEN
    2. LIBRARY RESOURCES - HIDE





    View Slide

  109. @MOLSJEROEN
    2. LIBRARY RESOURCES - UNIQUE NAME
    // library/build.gradle
    android {
    ...
    resourcePre
    fi
    x 'mylib_'
    }

    View Slide

  110. @MOLSJEROEN
    3. COMPATIBILITY: SOURCE
    PROJECT
    MAVEN
    CUSTOMER APP
    LIBRARY V1

    View Slide

  111. @MOLSJEROEN
    3. COMPATIBILITY: SOURCE
    PROJECT
    MAVEN
    CUSTOMER APP
    LIBRARY V2

    View Slide

  112. @MOLSJEROEN
    3. COMPATIBILITY: SOURCE
    class CustomerApp
    {

    fun useLibrary() = Library().initialize(
    )

    }

    // Library v1
    class Library {
    fun initialize() = Database().initialize()
    }

    View Slide

  113. @MOLSJEROEN
    3. COMPATIBILITY: SOURCE
    class CustomerApp
    {

    fun useLibrary() = Library().initialize(
    )

    }
    // Library v2
    class Library {
    fun initialize(useCaching: Boolean) = Database().initialize()
    }

    View Slide

  114. @MOLSJEROEN
    3. COMPATIBILITY: SOURCE
    class CustomerApp {
    fun useLibrary() = Library().initialize()
    }
    // Library v2
    class Library {
    fun initialize(useCaching: Boolean) = Database().initialize()
    }
    no value passed for parameter useCaching

    View Slide

  115. @MOLSJEROEN
    3. COMPATIBILITY: SOURCE
    class CustomerApp {
    fun useLibrary() = Library().initialize()
    }
    // Library v2
    class Library {
    fun initialize(useCaching: Boolean = true) = Database().initialize()
    }

    View Slide

  116. @MOLSJEROEN
    3. COMPATIBILITY: BINARY
    // Bytecode library v1
    public
    fi
    nal class com/jeroenmols/library/Library {
    public
    fi
    nal static initialize()
    V

    }
    // Bytecode library v2
    public
    fi
    nal class com/jeroenmols/library/Library {
    public
    fi
    nal static initialize(Z)
    V

    public static synthetic initialize$default(ZILjava/lang/Object;)
    V

    }

    View Slide

  117. @MOLSJEROEN
    3. COMPATIBILITY: BINARY
    // Bytecode library v
    1

    public
    fi
    nal class com/jeroenmols/library/Library {
    public
    fi
    nal static initialize()
    V

    }

    // Bytecode library v
    2

    public
    fi
    nal class com/jeroenmols/library/Library
    {

    public
    fi
    nal static initialize(Z)
    V

    public static synthetic initialize$default(ZILjava/lang/Object;)
    V

    }

    View Slide

  118. @MOLSJEROEN
    3. COMPATIBILITY: BINARY
    // Bytecode library v
    1

    public
    fi
    nal class com/jeroenmols/library/Library
    {

    public
    fi
    nal static initialize()
    V

    }

    // Bytecode library v
    2

    public
    fi
    nal class com/jeroenmols/library/Library {
    public
    fi
    nal static initialize(Z)
    V

    public static synthetic initialize$default(ZILjava/lang/Object;)
    V

    }

    View Slide

  119. @MOLSJEROEN
    3. COMPATIBILITY: BINARY
    OTHER LIBRARY
    MAVEN
    CUSTOMER APP
    LIBRARY V1
    LIBRARY V2

    View Slide

  120. @MOLSJEROEN
    3. COMPATIBILITY: BINARY
    OTHER LIBRARY
    MAVEN
    APP
    < FORCED >

    LIBRARY V2
    LIBRARY V2

    View Slide

  121. @MOLSJEROEN
    3. COMPATIBILITY: BINARY
    Maintaining Compatibility in Kotlin libraries by Márton Braun

    https://zsmb.co/maintaining-compatibility-in-kotlin-libraries/


    Public API challenges in Kotlin by Jake Wharton


    https://jakewharton.com/public-api-challenges-in-kotlin/

    View Slide

  122. @MOLSJEROEN
    3. COMPATIBILITY: GRADLE PLUGIN
    // top level build.gradle
    plugins {
    id 'org.jetbrains.kotlinx.binary-compatibility-validator' version '0.8.0-RC'
    }
    apiValidation {
    ignoredPackages += ["com.jeroenmols.library.internal",
    “com.jeroenmols.library.databinding"]
    ignoredClasses += ["com.jeroenmols.library.BuildCon
    fi
    g"]
    ignoredProjects += ["app"]
    }

    View Slide

  123. @MOLSJEROEN
    3. COMPATIBILITY: GRADLE PLUGIN
    // Snapshot current API into api subfolder
    $ ./gradlew apiDum
    p

    // Verify current API against snapshot
    $ ./gradlew apiCheck

    View Slide

  124. @MOLSJEROEN
    4. MINIMIZING API SURFACE
    • Easy to learn


    • Hard to make mistakes


    • Reduce maintenance


    • Avoid deprecation


    • Easier to change

    View Slide

  125. @MOLSJEROEN
    4. MINIMIZE API SURFACE
    // build.gradle
    fi
    le of each module
    android {
    ...
    kotlinOptions {
    freeCompilerArgs += '-Xexplicit-api=strict'
    }
    }
    // library
    class Library {
    fun initialize() { ... }
    }
    Visibility must be specified in explicit API mode

    View Slide

  126. @MOLSJEROEN
    4. MINIMIZE API SURFACE
    // build.gradle
    fi
    le of each module
    android {
    ...
    kotlinOptions {
    freeCompilerArgs += '-Xexplicit-api=strict'
    }
    }
    // librar
    y

    class Library
    {

    fun initialize() { ...
    }

    }

    View Slide

  127. @MOLSJEROEN
    4. MINIMIZE API SURFACE
    // build.gradle
    fi
    le of each modul
    e

    android
    {

    ..
    .

    kotlinOptions
    {

    freeCompilerArgs += '-Xexplicit-api=strict
    '

    }

    }
    // library
    class Library {
    fun initialize() { ... }
    }
    Visibility must be specified in explicit API mode

    View Slide

  128. @MOLSJEROEN
    5. EXPERIMENTAL APIS
    @RequiresOptIn(level = RequiresOptIn.Level.ERROR,
    message = "This is an experimental API.")
    @Retention(AnnotationRetention.BINARY)
    @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
    public annotation class ExperimentalMyLibraryApi

    View Slide

  129. @MOLSJEROEN
    5. EXPERIMENTAL APIS
    @RequiresOptIn(level = RequiresOptIn.Level.ERROR,
    message = "This is an experimental API.")
    @Retention(AnnotationRetention.BINARY
    )

    @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION
    )

    public annotation class ExperimentalMyLibraryApi

    View Slide

  130. @MOLSJEROEN
    5. EXPERIMENTAL APIS
    // library
    @ExperimentalMyLibraryApi
    fun doSomething()
    // ap
    p

    @OptIn(ExperimentalMyLibraryApi::class
    )

    override fun myFunction()
    {

    doSomething(
    )

    }

    View Slide

  131. @MOLSJEROEN
    5. EXPERIMENTAL APIS
    // librar
    y

    @ExperimentalMyLibraryAp
    i

    fun doSomething(
    )

    // app
    @OptIn(ExperimentalMyLibraryApi::class)
    override fun myFunction()
    {

    doSomething(
    )

    }

    View Slide

  132. @MOLSJEROEN
    5. EXPERIMENTAL APIS
    @kotlin.RequiresOptIn(
    level = kotlin.RequiresOptIn.Level.ERROR,
    )
    public annotation class ExperimentalMyLibraryAp
    i

    @androidx.annotation.RequiresOptIn(
    level = androidx.annotation.RequiresOptIn.Level.ERROR,
    )
    public annotation class ExperimentalMyLibraryApi

    View Slide

  133. @MOLSJEROEN
    6. DEPRECATION - REPLACE
    class Library
    {

    @Deprecated(message = “Please use the version with caching options”,
    replaceWith = ReplaceWith(“initialize(true)"))
    fun initialize(
    )

    fun initialize(useCaching: Boolean
    )

    }

    View Slide

  134. @MOLSJEROEN
    6. DEPRECATION - HIDE
    class Library
    {

    @Deprecated(message = “Please use the version with caching options”,
    level = DeprecationLevel.HIDDEN)
    fun initialize(
    )

    fun initialize(useCaching: Boolean
    )

    }

    View Slide

  135. @MOLSJEROEN
    7. PROGUARD
    android {
    defaultCon
    fi
    g {
    ...
    consumerProguardFiles 'consumer-rules.pro'
    }

    }

    View Slide

  136. WRAP UP

    View Slide

  137. @MOLSJEROEN
    DRIVE INNOVATION


    TOOLS ARE LACKING


    MINIMIZE API SURFACE


    LIMIT MODULARISATION


    REDUCE DEPENDENCIES

    View Slide

  138. @MOLSJEROEN
    HTTPS://JEROENMOLS.COM/BLOG

    View Slide

  139. @MOLSJEROEN
    IMAGE CREDITS
    Welcome image by James A. Molnar


    https://unsplash.com/photos/R8t7MjLXy2s


    Font awesome


    https://fontawesome.com/

    View Slide

  140. @MOLSJEROEN
    FURTHER READS
    Maintaining compatibility in Kotlin libraries by Márton Braun


    The hidden Kotlin gem: Deprecations with ReplaceWith by Marc Reichelt


    Publishing libraries to Maven Central in 2021 by Marton Braun


    Java interoperability policy for major version updates by Jake Wharton


    Public API challenges in Kotlin by Jake Wharton


    How to publish and distribute your Android library by Marco Gomiero


    What is a diamond dependency con
    fl
    ict by Google


    All about Opt-in annotations by Márton Braun

    View Slide

  141. MOLSJEROEN

    View Slide