Apps for Android devices
All our apps are developed natively for every platform. This means that the app is tailor-made for each platform (iOS and Android). This way our apps can make optimal use of the functionalities of each device and the quality of the User Experience is guaranteed.
We develop Android apps in Android Studio. Android Studio gives the perfect support to create Android apps. This so-called IDE (integrated development environment) helps with hotkeys (shortkeys), autocomplete and various other plugins. Examples of plugins are the GraphQL plugin and the Firebase Testlab plugin.
Android Studio is installed with the so-called AVD manager (Android Virtual Device). Using this tool we can install different Android device emulators with different OS versions. This way we can test an app on any device without having that specific device.
Git (version control)
Git is a version control system that we use to manage our source code. Every change in the code is added to a repository through a commit. All commits are stored locally. Once the code is ready the committed commits are pushed to the git remote. We use Bitbucket for this. This structure ensures that the source code with associated changes can be viewed by the entire team. Git also makes collaboration easier, thanks to pull requests. Pull requests make sure that source code can be checked and assessed. Thanks to git, backups and old versions of the code are always available. When adding new 'features' or 'bug fixes', developers can work on a separate branch.
In the past all Android apps were written in Java, but nowadays Kotlin is the recommended language to develop an Android app.
It has many advantages over Java. For example, the chance of so-called NPEs (NullPointerExceptions) is reduced by adding optionals. Kotlin also uses features borrowed from functional languages. With functions such as
groupBy you can achieve the same thing in far fewer lines of code than in Java. In many cases, these functions themselves are faster than complex
while loops. And as in most modern languages, there is also type inference, which allows the compiler to figure out the type of a variable itself. In addition, there is better support for using immutables.
In addition to these examples, we now also work as much as possible with Kotlin Coroutines that help implement asynchronous functions. Instead of passing callbacks, which produces a lot of boilerplate code, you have the option of defining blocking (
suspend) functions with the help of coroutines. This leads to less and clearer code!
Nowadays it's impossible to develop apps without them being properly tested using unit and UI tests.
To write unit tests we use JUnit and Mockito. These are the most commonly used frameworks to test Android apps. With JUnit we define tests, set the desired assertions and test whether our class is functioning properly. Mockito is a helpful framework used to make so-called mocks of classes. By defining mocks in our tests, tests are not subject to the the behavior of dependencies. This way we can concentrate on the things we want to test and the unit tests remain small and fast.
In addition to unit tests, we also write automated UI tests. To be sure that apps give the desired results on a large number of devices, we use Espresso and UIAutomator. With these frameworks it is possible to describe a test script. When executing the UI tests, the script is executed, so we can see what the behavior of the app is.
We use Firebase Testlab to automate our tests. This allows us to send an app to servers that will then test it against a wide range of (real!) devices. When the tests are done it is possible to see videos, screenshots and logs with the results. This way we can find bugs that we can fix before we go live.
To be able to develop apps quickly, it is good practice to use a library every now and then. Android Jetpack is a set of libraries created by Google that helps us develop an app faster. Examples of libraries that we often use are Data Binding in combination with LiveData, Navigation and ViewModels.
With Data Binding you can indicate in your XML file which elements change when the underlying data is modified. This is done in combination with LiveData, which is an implementation of the Observable design pattern.
To make optimal use of MVVM, we use ViewModels. View models contain data necessary to render a view. The Jetpack implementation of view models retains this data, even if someone pauses an app (by pressing the home button, for example). This can be useful if you don't want to retrieve data every time you open the app again. Moreover, view models are separate from
Context and are therefore easy to test!
Finally, we also use the new way of navigating in Android, the Navigation library. Using this library developers can define a graph that describes the structure of an app.
In addition to the Jetpack libraries, we occasionally use 3rd party libraries. Examples of libraries we use regularly are Retrofit for network requests, Glide for rendering and caching images, and Dagger for dependency injection.
During the development of an app, it often happens that you want to display a list of a certain type of object. We do this in a so-called RecyclerView. To easily add a RecyclerView, we have developed a generic recyclerview adapter. Read more about this in this blog post.
After development is completed for an app, the acceptance test for the customer and/or end users starts. This way, customers can check whether the app has turned out to be the way they envisioned.
Google Play offers excellent support for this. We mainly use the Internal test channel (for the employees of 9to5) and the Alpha channel (for the customer). Registering as a tester is easy via a Google Play link. After the customer has registered as a tester, they can simply download the app from the Play Store. Any updates will also be shared via the same channel.
Firebase is a platform used in developing mobile applications. It consists of many products that we use a lot.
We use Crashlytics to always be aware of possible crashes in the applications we make. In the event of a crash, an (anonymous) report is sent to Firebase in which the crash properties are described. This way we can see what went wrong and we can solve the problem more efficiently and more thoroughly!
We also use Firebase Analytics. With this we can see how users use the app and certain features.
Finally, we also occasionally use other Firebase products, such as the Firestore, Remote config and A/B tests.
We try to save as much time as possible on every project by using the right tools. Bitrise is an example of this. Bitrise is a CI (Continuous Integration) tool that automates a lot of work. The moment we make an adjustment in an app that we want to deploy to the Google Play Store, it will be picked up by Bitrise. Bitrise will then perform the associated unit and UI tests, build and sign the app, generate an AAB (Android App Bundle) and finally send it to the Google Play Store.