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

How to Build Awesome Android Libraries (360 AnDev 2021)

How to Build Awesome Android Libraries (360 AnDev 2021)

This talk is a collection of advice about building Android libraries or SDKs, highlighting some of the less obvious things you can do to make your library great.

Many of the recommendations will also make your life easier in any multi-module project, even if you're not publishing libraries anywhere.

All of this is based on my own open-source library development work - which I do both in my free time and at my day job working on the Stream Chat Android SDK.

More info and resources: https://zsmb.co/appearances/360-andev-2021/

4047c64e3a1e2f81addd4ba675ddc451?s=128

Marton Braun

July 22, 2021
Tweet

Transcript

  1. zsmb.co zsmb13 How to Build Awesome Android Libraries Márton Braun

  2. Introduction

  3. Introduction RainbowCake rainbowcake/rainbowcake Krate AutSoft/Krate MaterialDrawerKt zsmb13/MaterialDrawerKt requireKTX zsmb13/requireKTX

  4. GetStream/stream-chat-android › Open source chat SDK

  5. GetStream/stream-chat-android › Open source chat SDK › Low-level client with

    real-time events
  6. GetStream/stream-chat-android › Open source chat SDK › Low-level client with

    real-time events
  7. GetStream/stream-chat-android › Open source chat SDK › Low-level client with

    real-time events › Offline support › Pre-built UI components
  8. GetStream/stream-chat-android › Open source chat SDK › Low-level client with

    real-time events › Offline support › Pre-built UI components
  9. GetStream/stream-chat-android › Open source chat SDK › Low-level client with

    real-time events › Offline support › Pre-built UI components › Kotlin-first APIs
  10. GetStream/stream-chat-android › Open source chat SDK › Low-level client with

    real-time events › Offline support › Pre-built UI components › Kotlin-first APIs › Free for makers!
  11. GetStream/stream-chat-android

  12. TL;DW Minimize ALL the things!

  13. TL;DW Minimize ALL the things! › Features of the library

  14. TL;DW Minimize ALL the things! › Features of the library

    › Public API
  15. TL;DW Minimize ALL the things! › Features of the library

    › Public API › Dependencies
  16. TL;DW Minimize ALL the things! › Features of the library

    › Public API › Dependencies › Performance impact
  17. TL;DW Minimize ALL the things! › Features of the library

    › Public API › Dependencies › Performance impact › Enabled features by default
  18. TL;DW Minimize ALL the things! › Features of the library

    › Public API › Dependencies › Performance impact › Enabled features by default › Requirements
  19. Public API

  20. Public API › Minimal API

  21. Public API › Minimal API Item 15: Minimize the accessibility

    of classes and members
  22. Public API › Minimal API › Deprecations

  23. Public API › Minimal API › Deprecations

  24. Public API › Minimal API › Deprecations › Easy to

    use
  25. Public API › Minimal API › Deprecations › Easy to

    use › Hard to misuse
  26. Public API zsmb.co/talks/mastering-api-visibility/ › Minimal API › Deprecations › Easy

    to use › Hard to misuse › Controlling visibility
  27. Public API › Minimal API › Deprecations › Easy to

    use › Hard to misuse › Controlling visibility › Validating
  28. Public API › Minimal API › Deprecations › Easy to

    use › Hard to misuse › Controlling visibility › Validating
  29. Public API › Minimal API › Deprecations › Easy to

    use › Hard to misuse › Controlling visibility › Validating › Dependencies interface ChatClient { suspend fun sendMessage(message: Message): Message }
  30. Public API › Minimal API › Deprecations › Easy to

    use › Hard to misuse › Controlling visibility › Validating › Dependencies public interface Call<T> { public fun execute(): Result<T> public fun enqueue(callback: Callback<T>) } public suspend fun <T> Call<T>.await(): Result<T>
  31. Dependencies

  32. Dependencies › Minimal dependencies

  33. Dependencies › Minimal dependencies › Size

  34. Dependencies › Minimal dependencies › Size › Many libraries per

    app
  35. Dependencies › Minimal dependencies › Size › Many libraries per

    app › Library present in multiple apps
  36. Dependencies › Minimal dependencies › Size › Many libraries per

    app › Library present in multiple apps › Complexity
  37. Dependencies › Minimal dependencies › Size › Many libraries per

    app › Library present in multiple apps › Complexity › Version conflicts
  38. Dependencies Library 2 Library 1 Appcompat 1.4 alpha Appcompat 1.2

    App › Minimal dependencies › Size › Many libraries per app › Library present in multiple apps › Complexity › Version conflicts
  39. Version updates

  40. Version updates

  41. implementation vs api Library 1 App

  42. implementation vs api Library 1 Library 2 App implementation

  43. implementation vs api Library 1 Library 2 App implementation

  44. implementation vs api Library 1 Library 2 App api Library

    1 Library 2 App implementation
  45. library.aar

  46. library.aar resources layout.xml values.xml

  47. library.aar resources layout.xml values.xml AndroidManifest.xml

  48. library.aar resources layout.xml values.xml AndroidManifest.xml classes.jar

  49. library.aar resources layout.xml values.xml AndroidManifest.xml classes.jar R.txt

  50. library.aar resources layout.xml values.xml AndroidManifest.xml classes.jar R.txt library- sources.jar library-

    javadoc.jar *.kt *.html
  51. The POM file library.pom <?xml version="1.0" encoding="UTF-8"?> <project> <groupId>io.getstream</groupId> <artifactId>stream-chat-android-client</artifactId>

    <version>4.12.0</version> <packaging>aar</packaging> <name>stream-chat-android-client</name> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> <version>1.5.10</version> <scope>runtime</scope> </dependency> ... </dependencies> </project> library.aar resources layout.xml values.xml AndroidManifest.xml classes.jar R.txt library- sources.jar library- javadoc.jar *.kt *.html
  52. The POM file library.pom <?xml version="1.0" encoding="UTF-8"?> <project> <groupId>io.getstream</groupId> <artifactId>stream-chat-android-client</artifactId>

    <version>4.12.0</version> <packaging>aar</packaging> <name>stream-chat-android-client</name> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> <version>1.5.10</version> <scope>runtime</scope> </dependency> ... </dependencies> </project>
  53. The POM file library.pom <?xml version="1.0" encoding="UTF-8"?> <project> <groupId>io.getstream</groupId> <artifactId>stream-chat-android-client</artifactId>

    <version>4.12.0</version> <packaging>aar</packaging> <name>stream-chat-android-client</name> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> <version>1.5.10</version> <scope>runtime</scope> </dependency> ... </dependencies> </project>
  54. The POM file library.pom <?xml version="1.0" encoding="UTF-8"?> <project> <groupId>io.getstream</groupId> <artifactId>stream-chat-android-client</artifactId>

    <version>4.12.0</version> <packaging>aar</packaging> <name>stream-chat-android-client</name> <dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib</artifactId> <version>1.5.10</version> <scope>runtime</scope> </dependency> ... </dependencies> </project>
  55. Repositories MavenCentral Library 1

  56. Repositories MavenCentral Jitpack Jcenter Library 3 Library 2 Library 1

  57. Repositories MavenCentral Jitpack Jcenter Library 3 Library 2 Library 1

  58. Repositories App MavenCentral Jitpack Jcenter Library 3 Library 2 Library

    1
  59. Repositories App MavenCentral Jitpack Jcenter Library 3 Library 2 Library

    1
  60. Repositories App MavenCentral Jitpack Jcenter Library 3 Library 2 Library

    1
  61. Repositories App MavenCentral Jitpack Jcenter Library 3 Library 2 Library

    1
  62. Multi-module libraries my-awesome-lib

  63. Multi-module libraries my-awesome-lib

  64. Multi-module libraries my-awesome-lib my-awesome-lib-plus my-awesome-lib-core

  65. Multi-module libraries my-awesome-lib-plus my-awesome-lib-core my-awesome-lib

  66. rx Multi-module libraries my-awesome-lib-plus my-awesome-lib-core

  67. Multi-module libraries plus rxjava my-awesome-lib-rx my-awesome-lib-core

  68. Fat AARs ui-components core offline client

  69. Fat AARs ui-components core offline client App

  70. Fat AARs ui-components core offline client App

  71. Fat AARs ui-components core offline client App

  72. Fat AARs

  73. Fat AARs

  74. Fat AARs

  75. Fat AARs "At the moment we are talking with Gradle

    about being able to merge dependencies from multiple projects (that are being bundled), as that part is a problem in the JVM ecosystem as well. Once that is in, we can focus on the Android-specific features (Android resources, manifest etc.)." 2020-12-02
  76. Fat AARs fat-aar-plugin vigidroid/fat-aar-plugin fataar-gradle-plugin Mobbeel/fataar-gradle-plugin fat-aar-android kezong/fat-aar-android android-fat-aar adwiv/android-fat-aar

  77. Startup impact

  78. Startup impact › Don’t hog startup › Competing with other

    libraries
  79. Startup impact › Don’t hog startup › Competing with other

    libraries › Initialize on-demand
  80. Startup impact › Don’t hog startup › Competing with other

    libraries › Initialize on-demand › App Startup
  81. Logging

  82. Logging › Don’t log by default

  83. Logging › Don’t log by default val client = ChatClient.Builder("apiKey",

    context) .logLevel(ChatLogLevel.ALL) .build()
  84. Logging › Don’t log by default › Don’t enable any

    feature by default val client = ChatClient.Builder("apiKey", context) .logLevel(ChatLogLevel.ALL) .build()
  85. Requirements › Minimal requirements › Make integrating your library easy

    › Don’t force things on your clients
  86. API level App Library 1 Library 2 Library 3 API

    21 API 21 API 19 API 15
  87. API level App Library 1 Library 2 Library 3 API

    21 API 21 API 19 API 15
  88. API level App Library 1 Library 2 Library 3 API

    21 API 21 API 23 API 15
  89. API level App Library 1 Library 2 Library 3 API

    23 API 21 API 23 API 15
  90. Permissions & features library.aar resources layout.xml values.xml AndroidManifest.xml classes.jar R.txt

  91. Permissions & features <?xml version="1.0" encoding="utf-8"?> <manifest> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"

    /> <uses-feature android:name="android.hardware.bluetooth" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CAMERA" /> <application> <provider /> <activity /> <activity /> </application> </manifest> library.aar resources layout.xml values.xml AndroidManifest.xml classes.jar R.txt
  92. Permissions & features <?xml version="1.0" encoding="utf-8"?> <manifest> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"

    /> <uses-feature android:name="android.hardware.bluetooth" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CAMERA" /> <application> <provider /> <activity /> <activity /> </application> </manifest> AndroidManifest.xml
  93. <?xml version="1.0" encoding="utf-8"?> <manifest> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-feature android:name="android.hardware.bluetooth"

    /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CAMERA" /> <application> <provider /> <activity /> <activity /> </application> </manifest> Permissions & features AndroidManifest.xml
  94. <?xml version="1.0" encoding="utf-8"?> <manifest> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-feature android:name="android.hardware.bluetooth"

    /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CAMERA" /> <application> <provider /> <activity /> <activity /> </application> </manifest> Permissions & features AndroidManifest.xml
  95. <?xml version="1.0" encoding="utf-8"?> <manifest> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-feature android:name="android.hardware.bluetooth"

    /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CAMERA" /> <application> <provider /> <activity /> <activity /> </application> </manifest> Permissions & features AndroidManifest.xml
  96. Resource prefixes android { resourcePrefix 'stream_ui_' }

  97. Resource prefixes <?xml version="1.0" encoding="utf-8"?> <resources> <color name="stream_ui_accent_blue">#005FFF</color> <color name="stream_ui_accent_red">#FF3742</color>

    <color name="stream_ui_ ">#20E070</color> </resources> accent_green android { resourcePrefix 'stream_ui_' }
  98. Resource prefixes android { resourcePrefix 'stream_ui_' } <?xml version="1.0" encoding="utf-8"?>

    <resources> <color name="stream_ui_accent_blue">#005FFF</color> <color name="stream_ui_accent_red">#FF3742</color> <color name=" ">#20E070</color> </resources> accent_green
  99. Resource prefixes android { resourcePrefix 'stream_ui_' } <?xml version="1.0" encoding="utf-8"?>

    <resources> <color name="stream_ui_accent_blue">#005FFF</color> <color name="stream_ui_accent_red">#FF3742</color> <color name=" ">#20E070</color> </resources> accent_green Resource named 'accent_green' does not start with the project's resource prefix 'stream_ui_'; rename to 'stream_ui_accent_green' ?
  100. Resource prefixes android { resourcePrefix 'stream_ui_' } <?xml version="1.0" encoding="utf-8"?>

    <resources> <declare-styleable name="MessageInputView"> <attr name="streamUiAttachButtonEnabled" format="boolean" /> <attr name="streamUiAttachButtonIcon" format="reference" /> <attr name="streamUiMentionsEnabled" format="boolean" /> <attr name="streamUiMentionsIcon" format="reference" /> <attr name="streamUiAttachmentMaxFileSizeMb" format="integer" /> </declare-styleable> </resources>
  101. Resource prefixes android { resourcePrefix 'stream_ui_' }

  102. Private resources

  103. Private resources <?xml version="1.0" encoding="utf-8"?> <resources> <public name="mylib_app_name" type="string" />

    <public name="mylib_public_string" type="string" /> </resources>
  104. Explicitly public resources <?xml version="1.0" encoding="utf-8"?> <resources> <public name="mylib_app_name" type="string"

    /> <public name="mylib_public_string" type="string" /> </resources>
  105. Proguard library.aar resources layout.xml values.xml AndroidManifest.xml classes.jar R.txt

  106. Proguard ## Stream Chat Android Client Proguard Rules -keep class

    io.getstream.chat.android.client.api.* { *; } -keep class io.getstream.chat.android.client.api.models.* { *; } -keep class io.getstream.chat.android.client.api2.model.** { *; } -keep class io.getstream.chat.android.client.errors.* { *; } -keep class io.getstream.chat.android.client.events.* { *; } -keep class io.getstream.chat.android.client.models.* { *; } -keep class io.getstream.chat.android.client.parser.* { *; } -keep class io.getstream.chat.android.client.socket.* { *; } -keep class io.getstream.chat.android.client.utils.Result { *; } -keep class io.getstream.chat.android.client.utils.SyncStatus { *; } library.aar resources layout.xml values.xml AndroidManifest.xml classes.jar R.txt proguard.txt
  107. Proguard ## Stream Chat Android Client Proguard Rules -keep class

    io.getstream.chat.android.client.api.* { *; } -keep class io.getstream.chat.android.client.api.models.* { *; } -keep class io.getstream.chat.android.client.api2.model.** { *; } -keep class io.getstream.chat.android.client.errors.* { *; } -keep class io.getstream.chat.android.client.events.* { *; } -keep class io.getstream.chat.android.client.models.* { *; } -keep class io.getstream.chat.android.client.parser.* { *; } -keep class io.getstream.chat.android.client.socket.* { *; } -keep class io.getstream.chat.android.client.utils.Result { *; } -keep class io.getstream.chat.android.client.utils.SyncStatus { *; } proguard.txt
  108. Proguard ## Stream Chat Android Client Proguard Rules -keep class

    io.getstream.chat.android.client.api.* { *; } -keep class io.getstream.chat.android.client.api.models.* { *; } -keep class io.getstream.chat.android.client.api2.model.** { *; } -keep class io.getstream.chat.android.client.errors.* { *; } -keep class io.getstream.chat.android.client.events.* { *; } -keep class io.getstream.chat.android.client.models.* { *; } -keep class io.getstream.chat.android.client.parser.* { *; } -keep class io.getstream.chat.android.client.socket.* { *; } -keep class io.getstream.chat.android.client.utils.Result { *; } -keep class io.getstream.chat.android.client.utils.SyncStatus { *; } -dontoptimize -dontshrink proguard.txt
  109. Proguard android { buildTypes { release { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt’), 'proguard-rules.pro'

    consumerProguardFiles 'consumer-proguard-rules.pro' } debug { consumerProguardFiles 'consumer-proguard-rules.pro' } } }
  110. Proguard android { buildTypes { release { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt’), 'proguard-rules.pro'

    consumerProguardFiles 'consumer-proguard-rules.pro' } debug { consumerProguardFiles 'consumer-proguard-rules.pro' } } }
  111. Proguard android { buildTypes { release { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt’), 'proguard-rules.pro'

    consumerProguardFiles 'consumer-proguard-rules.pro' } debug { consumerProguardFiles 'consumer-proguard-rules.pro' } } }
  112. Testing

  113. Testing › Lots of responsibility

  114. Testing › Lots of responsibility › Diverse environments

  115. Testing › Lots of responsibility › Diverse environments › Can’t

    assume anything
  116. Testing › Lots of responsibility › Diverse environments › Can’t

    assume anything › Devices, architectures, OS versions, stores
  117. Testing › Lots of responsibility › Diverse environments › Can’t

    assume anything › Devices, architectures, OS versions, stores › Other frameworks and libraries
  118. Testing › Lots of responsibility › Diverse environments › Can’t

    assume anything › Devices, architectures, OS versions, stores › Other frameworks and libraries › Your code is not final
  119. Documentation

  120. Documentation › Have documentation

  121. Documentation › Have documentation › README

  122. Documentation › Have documentation › README › What it does

  123. Documentation › Have documentation › README › What it does

    › Setup instructions
  124. Documentation › Have documentation › README › What it does

    › Setup instructions › Screenshots
  125. Documentation › Have documentation › README › Code comments

  126. Documentation › Have documentation › README › Code comments

  127. Documentation › Have documentation › README › Code comments

  128. Documentation › Have documentation › README › Code comments ›

    Sample app
  129. Documentation › Have documentation › README › Code comments ›

    Sample app › Update strategy
  130. Publishing

  131. Publishing › Maven repositories

  132. Publishing › Maven repositories › Main options

  133. Publishing › Maven repositories › Main options › Jcenter

  134. Publishing › Maven repositories › Main options › Jcenter

  135. Publishing › Maven repositories › Main options › Jcenter

  136. Publishing › Maven repositories › Main options › Jcenter

  137. Publishing › Maven repositories › Main options › Jcenter ›

    Jitpack
  138. Publishing › Maven repositories › Main options › Jcenter ›

    Jitpack
  139. Publishing › Maven repositories › Main options › Jcenter ›

    Jitpack › Maven Central
  140. Publishing › Maven repositories › Main options › Jcenter ›

    Jitpack › Maven Central
  141. Publishing › Coordinates

  142. Publishing › Coordinates io.getstream:stream-chat-android-client:4.12.0

  143. Publishing › Coordinates io.getstream : stream-chat-android-client : 4.12.0 Group ID

    Version Artifact ID
  144. Publishing Group ID io.getstream : stream-chat-android-client : 4.12.0 Version Artifact

    ID › Coordinates
  145. Publishing › Coordinates io.getstream : stream-chat-android-client : 4.12.0 Group ID

    Version Artifact ID
  146. Publishing › Coordinates › Versioning io.getstream : stream-chat-android-client : 4.12.0

    Group ID Version Artifact ID
  147. Publishing › Coordinates › Versioning semver.org io.getstream : stream-chat-android-client :

    4.12.0 Group ID Version Artifact ID
  148. Publishing › Coordinates › Versioning › License

  149. Publishing › Coordinates › Versioning › License tldrlegal.com

  150. Release checklist

  151. Community

  152. Community › Open source vs source available

  153. Community › Open source vs source available › Address issues

    and discussions
  154. Community › Open source vs source available › Address issues

    and discussions › Take PR contributions
  155. Community › Open source vs source available › Address issues

    and discussions › Take PR contributions › Be open for feedback*
  156. Community › Open source vs source available › Address issues

    and discussions › Take PR contributions › Be open for feedback* “You're supposed to side with the community, but you should also have a say in what you're building” – Filip Babić
  157. Resources • Nishant Srivastava’s excellent talks  Things I wish

    I knew when I started building Android SDK/Libraries  https://www.youtube.com/watch?v=G-x9wRWwICo  Things I wish I knew when I started building Android Libraries Vol 2  https://www.youtube.com/watch?v=jQyt3HSmx2I • Mastering API Visibility in Kotlin  https://zsmb.co/talks/mastering-api-visibility/  Kotlin/binary-compatibility-validator  https://github.com/Kotlin/binary-compatibility-validator • Publishing Android libraries to MavenCentral in 2021  https://proandroiddev.com/publishing-android-libraries-to-mavencentral-in-2021-8ac9975c3e52
  158. Resources • Jeroen Mols’ library development articles  https://jeroenmols.com/blog/2020/10/28/library-gettingstarted/ 

    What is a library and how is it deployed  https://jeroenmols.com/blog/2020/11/04/library-modularization/  Publishing submodules, fat AARs, controlling access  https://jeroenmols.com/blog/2020/11/11/library-dependencies/  Dependency conflicts and version incompatibilities • More on fat AARs  Sam Edwards’ article  https://handstandsam.com/2018/07/13/why-we-need-fat-aars-for-android-libraries/  Open issue on Google’s issue tracker  https://issuetracker.google.com/issues/62121508
  159. zsmb13 zsmb.co/talks

  160. How to Build Awesome Android Libraries zsmb.co/talks zsmb13 Márton Braun

    › Minimize all the things! › Public API › Dependencies › Size and impact › Publishing and releases › Don’t forget the docs!