Blog Infos
Author
Published
Topics
Author
Published
Topics
Posted by: Marton Braun

Within the Stream Chat Android SDK project, we use the Kotlin binary compatibility validator plugin to keep track of all the changes we make to our public API. This is a first-party plugin by JetBrains, though it’s still experimental (it’s an incubator project by JetBrains on GitHub).

In this article, you’ll learn what the plugin does, how to set up and configure it, and how you can use it to make your library project better.

Core concept

Let’s start with a TL;DR of what the plugin does:

It generates <em>.api</em> files that describe the public API for each module, and if you make changes to public API, you have to explicitly update the content of the <em>.api</em> files (otherwise, checks will fail, alerting you of the accidental API change).

By doing this, it guarantees that developers on the project are always aware of the exact changes they’re making to public API. Since the plugin works on binary API, it will catch incompatible changes that might not be obvious when looking at source code. For example, when writing Kotlin code, using data classes, default implementations, or companion objects might create binary API that you’re usually unaware of.

Setup and configuration

The binary validator is available as a simple Gradle plugin, which makes setup really quick. Just add this code to your top level build.gradle file:

buildscript {
    dependencies {
        classpath 'org.jetbrains.kotlinx:binary-compatibility-validator:0.6.0'
    }
}apply plugin: 'binary-compatibility-validator'

With the plugin added, it’s now time to configure it. You can do this inside the apiValidation block. Here’s the configuration we use in our SDK as an example:

I am text block. Click edit button to change this text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.

apiValidation {
    ignoredPackages += [ // 1
            'com/getstream/sdk/chat/databinding',
            'io/getstream/chat/android/ui/databinding',
    ]    ignoredProjects += [ // 2
            'stream-chat-android-docs',
            'stream-chat-android-sample',
            'stream-chat-android-ui-components-sample',
            'stream-chat-android-test',
    ]    nonPublicMarkers += [ // 3
            'io.getstream.chat.android.core.internal.InternalStreamChatApi',
    ]
}

Let’s look at each of the options we’re using:

  1. ignoredPackages allows you to exclude some packages from validation. In our case, we don’t want to keep track of the files generated by View Binding. You could also use ignoredClasses to exclude individual classes by name.
  2. By default, the plugin will be enabled for all modules of a project. We use ignoredProjects to exclude non-published modules, like documentation and sample apps.
  3. The nonPublicMarkers entry allows you to specify any Kotlin Opt-in annotations that you’re using on API that’s not considered publicly available. To learn more about these, watch Mastering API Visibility in Kotlin.

You can then use the apiCheck Gradle task to verify that your current source code still has the same API as your committed .api files. This task will create up-to-date .api dumps in temporary build folders, and compare them to the .api files you have in your repository. If they don’t match, it will fail, and report the differences detected.

For example, see this error the task generates after moving a property from a data class’ constructor to its body:

Execution failed for task ':stream-chat-android-client:apiCheck'.
> API check failed for project stream-chat-android-client.
   @@ -218,9 +218,8 @@

   public final class io/getstream/chat/android/client/api/models/AutocompleteFilterObject : io/getstream/chat/android/client/api/models/FilterObject {
    public final fun component1 ()Ljava/lang/String;
  - public final fun component2 ()Ljava/lang/String;
  - public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lio/getstream/chat/android/client/api/models/AutocompleteFilterObject;
  + public final fun copy (Ljava/lang/String;)Lio/getstream/chat/android/client/api/models/AutocompleteFilterObject;
    public fun equals (Ljava/lang/Object;)Z
    public final fun getFieldName ()Ljava/lang/String;
    public final fun getValue ()Ljava/lang/String;

   You can run :stream-chat-android-client:apiDump task to overwrite API declarations

If those differences are intentional, you should run the apiDump task again, which updates the stored .api files, now including any changes in the API you’ve created by changing the source code. Running the apiCheck task at this point will complete successfully, as the sources and the .api files are in sync again.

This is the goal of the plugin: making developers explicitly run a task to update the API description files when they touch public API.

You should then commit the changes you’ve introduced to the .api files. These will show up in commits and on pull requests, allowing reviewers to easily tell when you’ve made changes to the binary API, and catch any unintentional changes.

Job Offers

Job Offers


    Senior Android Engineer – Big Release Team

    Zalando SE
    Berlin
    • Full Time
    apply now

    Delivery Lead / Scrum Master (m/w/d)

    Deutsche Post IT Services (Berlin) GmbH
    Berlin
    • Full Time
    apply now

    Android Developer

    Yoti Ltd
    Anywhere
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

,

Keep Rules in the Age of Kotlin

ProGuard has been the industry standard for obfuscating, shrinking and optimizing Java & Android apps for close to 20 years. ProGuard, and the compatible R8 shrinker, usually need some configuration since it’s not always technically possible to determine which entities should be processed. The configuration for these tools is based on `-keep` rules that use a…
READ MORE

Jobs

Git hooks and CI checks

The power of the plugin really shows if you have automated checks in place to make sure that whenever you change your public API, you’ve actually updated the .api files to match.

At Stream, we run the checks in two ways:

  • Using a pre-commit git hook in our repository that will run the apiChecktask (and automatically run apiDump for you if it fails) when you try to create a new commit in the repository.
  • With a GitHub Action step in our PR checks workflow that makes sure that the api files are up-to-date before we merge changes.
Limitations

This plugin is a binary validator. This means that it doesn’t track public API changes that don’t affect binary compatibility. For example, reified functions are part of source-level API but are not present in the binary API. Thankfully, changes in these should be obvious from the source file changes anyway (and they’re not very frequent).

There are also some known issues in the library, as you can see on GitHub. We’ve reported some of these ourselves, mostly for problems in respecting non-public markers, which we use extensively in our project (see #36 and #58).

Conclusion

Even with those limitations in mind, the binary compatibility validator plugin is a great tool for making sure that you don’t make any accidental changes in your public binary API when building a library. Check it out on GitHub.

You’ll also find our Android Chat SDK on GitHub, and you can try our In-App Messaging Tutorial to get started with it.

More library development topics you might be interested in:

Originally published at https://getstream.io.

Tags: Android, AndroidDev, Kotlin

 

View original article at:


Originally published: July 18, 2021

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Drag and Drop reordering in Recyclerview can be achieved with ItemTouchHelper (checkout implementation reference).…
READ MORE
blog
Modern mobile applications are already quite serious enterprise projects that are developed by hundreds…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu