Kotlin library development for Android/Java hints

Ivan Shafran
ProAndroidDev
Published in
4 min readMay 12, 2019

--

Photo by Iñaki del Olmo on Unsplash

Kotlin versions support

When we develop a regular app for Android we can choose any Kotlin version. Some teams prefer to use the latest version in order to try new features and tooling improvements. Some teams wait some time before switching to a new version. It can be linked to a release cycle or waiting for community feedback on the last version.

But when we develop a library on Kotlin we have to support both versions. Luckily Kotlin has good forward and backward compatibility.

Backward compatibility

It’s simple:

All binaries are backwards compatible, i.e. a newer compiler can read older binaries (e.g. 1.3 understands 1.0 through 1.2) [1]

But it only applies to fully stable APIs: std-lib, coroutines. Check [2] for the full list.

Forward compatibility

There is such statement:

Preferably (but we can’t guarantee it), the binary format is mostly forwards compatible with the next feature release, but not later ones (in the cases when new features are not used, e.g. 1.3 can understand most binaries from 1.4, but not 1.5). [1]

Not very specific but in practice, my library which targets 1.3 works fine with 1.2 and even 1.1 std-lib without any changes. And it’s not working with 1.0 due to function reference feature which I used in the library code.

But if we want more strict guarantees Kotlin compiler provides two useful flags: apiVersion and languageVersion.[3]

-language-version X.Y - compatibility mode for Kotlin language version X.Y, reports errors for all language features that came out later.[4]

-api-version X.Y - compatibility mode for Kotlin API version X.Y, reports errors for all code using newer APIs from the Kotlin Standard Library (including the code generated by the compiler).[4]

In Gradle I’ve configured it as following:

After that, you will probably see some compilation errors. In order to fix them, you should exclude new features usages and write useful extension/function/classes from 1.3 by yourself.

Library development version

You can use the latest Kotlin version in your development. Except for new language features(which we can’t use due to apiVersion and languageVersion) it also brings compiler and tooling improvements.

Dropping support of old versions

In my opinion, it is not healthy to support old versions too long. I would recommend maintaining stability only one version back. Also, you could check release dates in artifacts repository to decide which versions to support [5].

Kotlin std-lib, std-lib-jdk7 or std-lib-jdk8?

Mainly you should choose between them in the following order:

  1. std-lib (for Java 6)
  2. std-lib-jdk7 (for Java 7)
  3. std-lib-jdk8 (for Java 8)

If your code uses some features from jdk7(for example AutoCloseable useextension) and developer who uses your library provides std-lib, not std-lib-jdk7 or std-lib-jdk8 then his app will throw NoClassDefFoundError .

In Android, there is no need to support std-lib if you minSdk 19 at least. And there is no need to support std-lib-jdk7 if you source and target compatibility JavaVersion.VERSION_1_8.[6][7] But Android Studio doesn’t warning about that so try to support std-lib with fewer restrictions.

Package structure and visibility

A good library should hide all unnecessary classes, functions and properties from library users. Mostly you should prefer using private and internal modifiers for these situations.

In addition, it is recommended to move all your not public code in the package with name “internal”:

Also, by convention, packages named “internal” are not considered public API. [8]

Java compatibility

In most cases, Kotlin does all work for you. It creates get/setfor properties for example.

Currently, I’ve faced only one problem. If you declare a public property in companion from Java it is accessible via Sample.Companion.INSTANCE . To fix that use @JvmField annotation.

Kotlin dependency in pure Java project

An Android library which is distributed as aar doesn’t include its dependency. Therefore in pure Java project library users will get NoClassDefFoundError because your library depends on Kotlin std-lib. Thus you should write notices about that in the README file or docs.

But people don’t like to read docs a lot. I would suggest one trick below. Please share in comments your opinion is it a good practice or not?

Declare dependency checker:

Check dependency in the static initializer block:

And if someone forgets to include Kotlin std-lib dependency he will get the solution in a crash log not just error.

--

--