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/

Márton Braun

July 22, 2021
Tweet

More Decks by Márton Braun

Other Decks in Programming

Transcript

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

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

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

    real-time events › Offline support › Pre-built UI components › Kotlin-first APIs
  4. 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!
  5. TL;DW Minimize ALL the things! › Features of the library

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

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

    › Public API › Dependencies › Performance impact › Enabled features by default › Requirements
  8. Public API › Minimal API › Deprecations › Easy to

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

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

    use › Hard to misuse › Controlling visibility › Validating › Dependencies interface ChatClient { suspend fun sendMessage(message: Message): Message }
  11. 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>
  12. Dependencies › Minimal dependencies › Size › Many libraries per

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

    app › Library present in multiple apps › Complexity › Version conflicts
  14. 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
  15. 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
  16. 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>
  17. 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>
  18. 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>
  19. 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
  20. Startup impact › Don’t hog startup › Competing with other

    libraries › Initialize on-demand › App Startup
  21. Logging › Don’t log by default › Don’t enable any

    feature by default val client = ChatClient.Builder("apiKey", context) .logLevel(ChatLogLevel.ALL) .build()
  22. 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
  23. 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
  24. <?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
  25. <?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
  26. <?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
  27. 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
  28. 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' ?
  29. 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>
  30. 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
  31. 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
  32. 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
  33. Proguard android { buildTypes { release { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt’), 'proguard-rules.pro'

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

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

    consumerProguardFiles 'consumer-proguard-rules.pro' } debug { consumerProguardFiles 'consumer-proguard-rules.pro' } } }
  36. Testing › Lots of responsibility › Diverse environments › Can’t

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

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

    assume anything › Devices, architectures, OS versions, stores › Other frameworks and libraries › Your code is not final
  39. Community › Open source vs source available › Address issues

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

    and discussions › Take PR contributions › Be open for feedback*
  41. 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ć
  42. 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
  43. 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
  44. 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!