Lottie is a powerful library that parses After Effects animations and makes mobile (and web) apps more user friendly, enhancing them with beautiful animations and interactions.
It’s common for Android projects to fail during build due to incompatible versions of Lottie, or any other library, used by different dependencies.
However, these solutions can lead to runtime issues, including crashes.
Using a resolution strategy that forces a version in a conflict can lead to runtime error.
A more robust way to handle this issue is through Dependency Shadowing.
Dependency Shadowing involves taking a dependency and renaming its package to avoid conflicts. You might think about forking the repository and changing the package name, but there’s a more efficient approach that doesn’t require maintaining a fork.
There is a well-known Java plugin that does this shadowing but doesn’t work quite well will Android projects. So in order to have a better experience in Android development, a Gradle plugin should be created.
Dependency Shadowing
Gradle comes to our rescue
We’ll create a Gradle plugin that enables us to easily shadow dependencies like this:
Here, shadowAARImplementation is a configuration that processes the dependency, shadows it, and adds it to your project. In order to create our plugin. let’s create a buildSrc folder inside your Android project, and inside the folder a structure like the one showed below.
The content of the AddShadowedAARDependenciesPlugin.kt can be found at my public gist.
Once that is done, add a configuration block in order to rename the package of (Lottie) dependency, with a custom one. I will use for this example com.eaceto.shadowed.lottie
If you sync & build your Android project you will find a lot of error related to missing com.airbnb.lottie. This is because now you will find all the Lottie classes under com.eaceto.shadowed.lottie.
Adopting Lottie Compose
The above solution works great for Lottie and other libraries but the GradleUp/Shadow plugin has a few problems with top-level functions. The Lottie-Compose package is full of these functions, so the workaround is:
Copy all the Lottie Compose functions to a package in your code
So it seems like desktop applications are dead, or the were… I don’t know. I’d rather a full native app instead of a web-based app. EVERYWHERE! And that includes desktop apps. Electron.js based apps are fast to develop, easy to maintain, and cross platform, but they s*ck$. They are hungry for resources, CPU and memory, as they are based on the always-hungry (Chrome) browser.
So, a few solutions come to scene in the last few years. The most famous I believe it’s Flutter, it covers mobile and desktop, but also web. But it’s not the only one! JetBrains released Compose for Desktop, which targets macOS, Linux and Windows. It’s a port of Android’s Compose for Desktop, what makes this a great alternative for multiplatform apps. If we add Kotlin Multiplatform to this equation for creating Business Logic that can also run on iOS, Kotlin seems like a great starting point for fully native desktop and mobile apps.
Web-based multiplatfom solutions are hungry for resources. Native apps are more resource-efficient, load faster, work better in offline mode.
Similar to the previous article, I will explore the development process of a Linux app using Kotlin Compose, and also will run that app on macOS. Take into consideration that Kotlin Compose is yet an experimental feature.
System Requirements
The only requirement for developing apps using Compose for Desktop, is Kotlin 1.4.20 or newer. A Java Runtime Environment is of course required for this JVM language, and even Kotlin can run on JRE 7 (for example) it’s recommended to use an as new as possible Java Virtual Machine, for example JVM 11 or 13.
At the time of this post there is a bug in JDK 14 that doesn’t allow to generate a Debian package in Linux. If you are using Linux, please switch to either JDK 11 (LTS) or JDK 15.
IntelliJ IDE, made by JetBrains, supports creating Kotlin Compose apps out-of-the-box. Two flavours are available:
Desktop
Multiplatform
Desktop targets macOS, Linux and Windows using Compose, while Multiplatform adds Android support.
In this tutorial, a desktop-only app will be created.
Getting Started
When the IDE finished starting up the project, all dependencies are downloaded and the index is updates, a single Main.kt file will be present in your source folder. The entry point of the app is a main function that returns a Window.
Packaging an application can be a little tricky, you can run the app everywhere using a gradle o maven task, but you can only create a package, a distributable installation file, in the same architecture of the target package. In other words, there is no “cross packaging”. You cannot create a Debian compatible -deb- file from macOS. So, in order to cover all the available flavours, you will need access to Windows, macOS and Debian.
Which, if you have a macOS machine, it’s pretty easy to archive using VirtualBox or any other Virtual Machine.
The package task generates a distributable file for the current platform. There are other related tasks, but I couldn’t successfully run any of the on a platform different that the current on.
$ ./gradlew package
> Task :packageDmg
WARNING: Using incubator modules: jdk.incubator.jpackage
The distribution is written to build/compose/binaries/main/dmg/DesktopApp-1.0.0.dmg
BUILD SUCCESSFUL in 11s
5 actionable tasks: 3 executed, 2 up-to-date
msi (Microsoft Installer) for Windows
deb (Debian package) for Debian/Ubuntu compatible
dmg (Disk Image File) for macOS
Inside generated DMG file
Artefacts
The very simple app that you see in this post needs 80MB of disk space, and generates a DMG file of nearly 42MB. Where only 1MB belongs to this application (assets and compiled code). The rest is just Kotlin libraries (coroutines, stdlib, material design, etc).
/Applications/DesktopApp.app/Contents/app
kimi@Kimis-Mac-mini /A/D/C/app> ls -la
total 37024
drwxr-xr-x 38 kimi admin 1216 Mar 23 14:34 ./
drwxr-xr-x 8 kimi admin 256 Mar 23 14:34 ../
-rw-r--r-- 1 kimi admin 104427 Mar 23 14:34 DesktopApp-1.0.0.jar
-rw-r--r-- 1 kimi admin 1522 Mar 23 14:34 DesktopApp.cfg
drwxr-xr-x 3 kimi admin 96 Mar 23 14:34 META-INF/
-rw-r--r-- 1 kimi admin 1522 Mar 23 14:34 MainKt$main$1$1$1$1$1$1.class
-rw-r--r-- 1 kimi admin 9533 Mar 23 14:34 MainKt$main$1$1$1$1$2$1.class
-rw-r--r-- 1 kimi admin 15698 Mar 23 14:34 MainKt$main$1$1$1$1.class
-rw-r--r-- 1 kimi admin 9692 Mar 23 14:34 MainKt$main$1$1.class
-rw-r--r-- 1 kimi admin 2157 Mar 23 14:34 MainKt$main$1.class
-rw-r--r-- 1 kimi admin 1292 Mar 23 14:34 MainKt.class
-rw-r--r-- 1 kimi admin 208635 Mar 23 14:34 animation-core-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 201364 Mar 23 14:34 animation-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 17536 Mar 23 14:34 annotations-13.0.jar
-rw-r--r-- 1 kimi admin 15414 Mar 23 14:34 desktop-jvm-0.2.0-build132.jar
drwxr-xr-x 4 kimi admin 128 Mar 23 14:34 drawable/
-rw-r--r-- 1 kimi admin 822462 Mar 23 14:34 foundation-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 357370 Mar 23 14:34 foundation-layout-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 1488745 Mar 23 14:34 kotlin-stdlib-1.4.20.jar
-rw-r--r-- 1 kimi admin 191485 Mar 23 14:34 kotlin-stdlib-common-1.4.20.jar
-rw-r--r-- 1 kimi admin 22355 Mar 23 14:34 kotlin-stdlib-jdk7-1.4.20.jar
-rw-r--r-- 1 kimi admin 16233 Mar 23 14:34 kotlin-stdlib-jdk8-1.4.20.jar
-rw-r--r-- 1 kimi admin 196243 Mar 23 14:34 kotlinx-collections-immutable-jvm-0.3.jar
-rw-r--r-- 1 kimi admin 1672388 Mar 23 14:34 kotlinx-coroutines-core-jvm-1.4.1.jar
-rw-r--r-- 1 kimi admin 10884 Mar 23 14:34 kotlinx-coroutines-swing-1.4.1.jar
-rw-r--r-- 1 kimi admin 1261868 Mar 23 14:34 material-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 674505 Mar 23 14:34 material-icons-core-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 584609 Mar 23 14:34 runtime-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 19435 Mar 23 14:34 runtime-dispatch-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 56960 Mar 23 14:34 runtime-saved-instance-state-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 248772 Mar 23 14:34 skiko-jvm-0.1.18.jar
-rw-r--r-- 1 kimi admin 8553451 Mar 23 14:34 skiko-jvm-runtime-macos-x64-0.1.18.jar
-rw-r--r-- 1 kimi admin 1378337 Mar 23 14:34 ui-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 38650 Mar 23 14:34 ui-geometry-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 304483 Mar 23 14:34 ui-graphics-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 311880 Mar 23 14:34 ui-text-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 81156 Mar 23 14:34 ui-unit-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 11744 Mar 23 14:34 ui-util-desktop-0.2.0-build132.jar
It may seems to much, but, remember that this application embeds a Java Runtime and a lot of libraries that make your live easier developing in Kotlin.
In comparison to Electron.js + React.js I believe that the artefact size is similar, but there is a huge improvement in running a Kotlin / JVM app.