Whilst programming environments in general are constantly evolving, Android is undoubtedly one of the fastest changing year on year (or even month on month!) Read more about the top 5 techniques to watch out for in the coming months.
When most of the Candyspace Android team attended Droidcon London 2019, the sheer variety of evolving technologies presented in talks was staggering. From Joe Birch’s accessible smart guitar to the upcoming Jetpack Compose library, the amount of innovative creations could easily have been overwhelming, if not for the majority of the community agreeing on a few core techniques.
So let’s have a look at some of those and explain why they should be implemented sooner rather than later, and provide first steps towards that goal. It’s worth pointing out that whilst implementing these techniques won’t “wow” your end users, they’ll help create the features that will, as well as making sure developers enjoy the codebase!
Kotlin is often seen as a successor to Java, and is sponsored by Google & JetBrains (Android Studio developer). Whilst Java has been the primary choice for Android apps since the beginning, the last few years have seen Kotlin skyrocket in popularity, now being used in almost 60% of the top 10,000 Google Play apps. C++ continues to be used in the rare situations where the low level native code needs to be accessed, but in all other situations Kotlin can replace Java.
Kotlin’s main advantage is the full interoperability with Java, meaning developers can migrate legacy code when possible instead of fully rewriting an entire app. The two languages are so compatible that Android Studio can even automatically convert from Java to Kotlin!
This compatibility combined with a much more concise syntax and hundreds of quality of life improvements has led Kotlin to be the 4th most “Loved” and 5th most “Wanted” language in StackOverflow’s 2019 developer survey, the highest among all mobile programming languages.
A good way to start migrating an existing app is to convert existing Java files to Kotlin as you modify them. Whilst this does mean code review will be trickier (e.g. potential conflicts) whilst your commonly edited files are converted, it ensures any side effects will be noticeable as the converted area is already under scrutiny.
At Candyspace we are currently at 86% Kotlin code (and always increasing), with the remaining 14% generally consisting of utility / conversion code that hasn’t changed in a few years.
Google’s AndroidX / Jetpack libraries are a collection of utilities, aimed at simplifying common app needs. For example, Room for an on-device database, or LiveData to update displays when the underlying data changes.
Instead of reinventing the wheel with each project, or relying on another developer open sourcing their implementation, the Jetpack libraries allow everyone to have access to the essentials. These libraries are updated very frequently, with new features as well as timely bug fixes. As the libraries are built to work together, implementing AndroidX libraries where possible helps minimise the amount of unexpected behaviour in the app.
Whilst using Jetpack libraries from the beginning of development can save hundreds of hours, it’s also possible to migrate an existing app to them. This can seem daunting, but given the libraries’ massive popularity, there are likely guides for transitioning away from another solution. At the very least, the underlying Android elements (views, fragments, etc) can be transitioned automatically.
At Candyspace we use Data Binding and ViewModel, with Room & Navigation likely being added soon.
3. Modular design
Historically, apps were built as one giant “app” module, that contained everything the entire app needed. Whilst this did make sharing resources easy, it did mean parts of the app couldn’t be reused for other apps / open source, and more importantly the entire codebase had to be recompiled when making changes.
If, instead, an app consists of many smaller modules, only the changed code needs to be recompiled, leading to much faster build times. Additionally, it opens the door to using advanced Android features such as Instant Apps (users can use part of your app without installing anything) and Dynamic Features (installing additional parts of the app when they are needed).
Splitting an existing app into modules can be painful, as previously hidden isolation issues are revealed (“What is DateUtility, and why does every class need it!?”), but once completed the codebase will be in a much healthier state. Plus, if a new app ever needs similar functionality, being able to instantly reuse existing modules will save time!
An example modular app architecture (source: author created!)
Whilst architecting a modular design can be tricky, I’ve previously written about some guiding principles inspired by Nikits Kozlov’s article on modularisation and build times. Plaid have also written an article on their experiences migrating to modular design.
At Candyspace we are fully modular, ensuring development is held up as little as possible by build times.
4. App bundles
When distributing an app to a user’s device using a traditional APK, all resources for every possible device have to be installed. This means 5 copies of every bitmap image (for different screen densities), multiple copies of libraries for different device architectures, and even multiple sets of values for margins and paddings.
When distributing using an app bundle, the APK the user downloads instead just contains the resources they actually need. This results in average app size reductions of 20%, with unoptimised apps receiving much more impressive reductions.
Examples of app size savings (source: Google I/O 2018)
App bundles have only been around for 18 months, but already over 25% of app installs are of apps using them! They are recommended by Google, and most apps will work with them without any work besides that required for app bundle signing on the Play Store.
At Candyspace we are in the process of migrating to app bundles, whilst being careful to avoid breaking existing workflows (Slack, QAing builds, non-Google Play installs). Alistair Sykes’ article is an excellent resource for migration, taking into account CI servers, Slack, and Google Play Internal App Sharing.
Yep, testing. Sure, it’s not a shiny new feature, or anything the user will actually see, but thoroughly testing your app to ensure reliability is absolutely essential on an app with an established user base. As crash rates can directly influence your Play Store rating (and definitely affect your ratings!), keeping it low is essential.
The testing pyramid (source: developer.android.com)
The three most common types of test (in descending order) for Android are:
Unit tests e.g. does my square root function return the square root?
These will make up the majority of your testing, and are used to ensure a specific piece of code (e.g. a function) works as intended. Once you’ve got confidence in a part, you can use it for…
Integration tests e.g. does my maths module work with my location module?
These tests ensure the areas of your code (modules, or layers) work correctly together. Once you know your app’s components talk to each other correctly, you can add…
Automated UI tests e.g can the user mark a location on the app?
These tests are the only ones that run on a device or emulator, and ensure that a full user journey works as expected. These are often much slower (as well as being more inconvenient to run) then the other types of test.
Google recommends aiming for test distributions of 70% unit, 20% integration, and 10% large, with the smaller percentages taking longer to execute, longer to maintain, and longer to implement.
The best resource for testing is the official documentation, as it provides an introduction to all the test types, as well as how to begin implementing them into your project.
At Candyspace we focus even more heavily on unit tests than the recommended percentage to ensure all new classes are going to behave predictably. We are also currently improving our automated UI tests to reduce reliance on manual testing.
Whilst in any area of programming there’ll be a hundred different opinions on the best way to solve a problem, Android has the advantage of an absolutely massive developer community, meaning the new, powerful techniques quickly spread to ubiquity. When asking strangers on the internet for help, there’s a lot higher chance of success if you ask about “Jetpack LiveData” instead of “the library the previous developer copied from his web developer friend, then converted”!
Being able to adapt to these constantly changing standards and refactor existing projects is essential for a healthy codebase. To stay up to date with Android developments and best practices, I recommend the Android Developers Blog, the /r/AndroidDev subreddit, and the Fragmented podcast.