Usage and deployment variability is one of the main causes of software complexity and is an intensively researched topic1. libGDX is no exception – programs built upon this framework are often deployed on many different kinds of systems, while these programs each use the framework in their own way. That is the strength of libGDX, but this strength also incurs costs. We look into the main causes of variability and how they interact. We also look into how the developers of libGDX deal with the variability inherent in the platform and how this is implemented in practice. Finally, we give our own opinions on how variability is dealt with in libGDX.
Feature model and variability
libGDX is by design a library offering many features a developer can choose to utilize. We will list ten variability features below.
- Backend Stated extensively throughout the blogposts, libGDX offers different platforms to publish your work to. Developers are free to choose which platform they select and also which platform they will not support. The developer can opt for a wider audience by offering different platforms. However, there is the chance that the developer must write platform-specific code2. The developer benefits from a wider audience, but does not benefit from writing more complex code. The end-user will benefit if the project is ported to many platforms.
- Extensions Different extensions can be imported by using the libGDX project setup or by manually adding them to the gradle dependency list34. These extensions offer the developer very powerful tools that work off the shelve. The developer will benefit from the variability of the extensions. Some extensions will not be useful for the project so should not be imported. However, others are extremely handy and save the developers enormous amounts of time. Not only does it save time, it is often a solid implementation of what the developers seek. If it is not exactly what the developers seek, then the majority of the components are still fully customizable. The end-users will also benefit, as the application could offer a proper implementation of, for example, a complex physics application without too many faults, if it had been implemented otherwise.
- 3rd party programs Although it is not exactly a main feature of libGDX, third parties help lift the implementation load of certain required features for an application. Instead of writing, for example, an ads integration, one can simply use a plugin that is already written by the community. The ones benefiting from the variability of implementing it yourself, or use an off-the-shelf implementation, are the same as extensions.
- Debugging There are different debugging methods implemented in libGDX5. This variability in debugging mainly comes from the fact that different platforms can be supported. If the developer wishes to debug, for example, on Android and on desktop, then it is possible with two different debugging setups. The debugging variability is mostly helpful for the developer, however, if the developer is able to properly debug a specific backend, then the end-user will also benefit if the application does work properly.
- Dependency Manager Normally the libGDX project uses Gradle for its dependency management6. The developer can also opt to use Maven instead7. This choice is up to the developer and should only be considered if the developer is more used to Maven as the dependency manager.
- JVM Languages Normally the libGDX project uses Java as the language for the entire project. However, the developer is allowed to choose a language per backend8. This variability is only handy for the developer, as it could choose the language they want.
- Asset Manager Using assets on various platforms might introduce memory leaks. In order to tackle the problem of memory leaks, libGDX implements its off-the-shelf asset manager9. It is also possible to create your own implementation. The variability is mostly advantageous to the end-user, as using the application does not cause memory leaks with the use of a correct implementation of an asset manager.
- Internationalization and Localization Applications can be written for broader audiences by using internationalization and localization, an entire application will automatically adapt to a provided language file10. This variability has benefits for the end-user, as they can select the language they are most familiar with and have a better understanding of the application.
- Coordinate Systems libGDX uses many different coordinate systems in parallel. There are different coordinate systems implemented for the camera, game world, HUD, et cetera11. This has the advantage that the application can easily adapt to different platforms, by simply changing the conversion between these coordinate systems. This variability has the advantage that the application can be ported to any backend and remain visually equal. The end-user also benefits from this variability, as the application is equal across devices and might allow for extensive settings, such as zooming in the HUD to make it better readable.
- File Handling Since an application can be ported to different platforms and the fact that each platform has its own specific method for file handling, could make it difficult for the developer to handle files12. A file handler is used to reduce the variability of different file handling mechanisms introduced in each platform. Only the developers have a benefit from this feature, as they are not required to create a file handling implementation for each platform specifically.
Incompatibilities under variability
libGDX is by design a library supporting many different platforms13. To support each platform, libGDX offers different backends for the developer to utilize. Each backend has its own workflow attached to it, forcing the developer to maintain each backend somewhat separately. For example, when using the HTML backend, the developer has to run and debug their code in different ways14. The HTML backend uses GWT15 (short for Google Web Toolkit), as opposed to LWJGL for desktop backends, to get its graphics. The supported graphics are highly dependent on the backend and hardware the application is run on; both OpenGL 2.0 and 3.0 are supported. On top of this, each backend does not fully support the same features as other backends. The previously discussed GWT backend has several limitations compared to the LWJGL (desktop) backend for example. A couple noteworthy limitations are:
- Some Java classes/features are not supported (for example
System.nanoTime, Java reflection, multithreading14).
- libGDX Bullet extension is not supported, Freetype extension requires an additional library16.
Naturally, not every device that is able to run the application will support all of the features. For example, a device might not have internet capabilities, no audio or even lack proper support for OpenGL ES graphics. Finally, the developer must take into account what type of device they are working with. When creating an application for Android for example, it is easy to assume that every Android phone has a camera of some sort. The same cannot be said of desktops, where cameras are not a standard feature. These things are of course not something libGDX themselves are concerned about, but are still things a developer should keep in mind when creating their application for multiple backends (and consequently, devices). libGDX can aid with this by simplifying feature detection and toggling and documenting how the developer can use this optimally.
The following image gives a broad overview of libGDX’s features. We previously discussed a few points of incompatibility, these are displayed as constraints in the feature model17.
There are two groups of stakeholders which need to be well-informed of how libGDX manages variability. The first group is that of developers that use the libGDX framework for their own applications. The second group is that of developers contributing to the libGDX framework itself.
For developers that use the libGDX framework for their own applications, it was found in the previous post that they can rely on good documentation in general18. In terms of variability, an important point is that libGDX is a framework that works cross-platform13. On the libGDX website, developers can see that a single API is used to target every platform. It is mentioned that writing platform-specific code is not needed at all for the developers that use the libGDX framework19.
For developers that contribute to the libGDX framework itself, there is also some documentation available regarding variability in platform on the libGDX website. Developers can read about how to run different types of tests20. There is also a small guide for developers, which explains how to build the natives for different platforms21.
Regarding variability in platform, a nice feature that is offered to developers that use the libGDX framework, is that they can choose when generating a new libGDX project which platforms they would like to target3. This makes it easy to develop applications for different platforms. The screen that the developer sees when creating a new project, is shown below.
The fundamental variability of libGDX, that of the target platforms, is realized using backend packages that implement or can interface with the common interfaces of the platform-independent code and the application. That it is fundamental can be seen from the fact that it is one of the main drivers for libGDX’s architecture24.
Each backend provides a loader, which serves as the application’s entry point and initializes libGDX for its target platform25. It is this loader that ensures that the resulting environment is a valid libGDX environment and therefore bears most of the effort of abstracting away this variability.
Because the loader is the entry point and eventually hands over control to the application, the resulting application is packaged separately for each target platform; the packaging step (often considered part of compiling) is thus the binding time.
Extensions are implemented in a more loose manner. They are also packaged separately and therefore bound at compile-time. Each extension simply imports more classes that may depend on the core libGDX interfaces or other extensions, but the extensions themselves are not registered in a specific way. They are only listed in a file
extensions.xml26, part of the libGDX Project Generator shown above, to provide metadata like a description and version number.
As discussed before, some variability inherent in libGDX cannot be known at compile time. For some instances of this, libGDX provides extensions to abstract away some of this variability. For example for gamepad support, there is an extension “gdx-controllers”27. The application developer still needs to handle cases where no gamepad is available, and how to process gamepad input events.
In general, features that are only known to be available at run-time have corresponding feature detection flags. In the case of gyroscopes (common on phones, but not available on all of them), one can query for support as follows28:
boolean gyroscopeAvailable = Gdx.input.isPeripheralAvailable(Peripheral.Gyroscope);
The application can then, dynamically, decide whether to use the gyroscope or not. libGDX does not make this more complicated, and it is against its own philosophy, as a developer-centric framework, to do so otherwise.
All in all, we feel libGDX is dealing well with its variability. Its cross-platform nature naturally gives rise to invariabilities between backends, but working with multiple different backends is made to be as smooth as possible. While the approach could be improved using inversion of control, where the backend is injected in the application instead of globally available, it is not necessary because generally programs require only one instance of each subsystem.
The support (and encouragement) to use other JVM languages is also quite nice to see. The wiki is extensive and the community is active, so getting support with the whole framework and its variability is easy.
Galster, Matthias & Zdun, Uwe & Weyns, Danny & Rabiser, Rick & Zhang, Bo & Goedicke, Michael & Perrouin, Gilles. (2017). Variability and Complexity in Software Design: Towards a Research Agenda. ACM SIGSOFT Software Engineering Notes. 41. 27-30. 10.1145/3011286.3011291. ↩︎
libGDX. (n.d.) Interfacing with platform specific code. Retrieved March 28, 2021, from https://github.com/libgdx/libgdx/wiki/Interfacing-with-platform-specific-code ↩︎
libGDX. (n.d.) libGDX extensions. Retrieved March 28, 2021, from https://github.com/libgdx/libgdx/wiki/Dependency-management-with-Gradle#libgdx-extensions ↩︎
libGDX. (n.d.) Debugging your application. Retrieved March 28, 2021, from https://github.com/libgdx/libgdx/wiki/Project-setup,-running-&-debugging#debugging-your-application ↩︎
libGDX. (n.d.) Creating a Project - What is gradle. Retrieved March 28, 2021, from https://libgdx.com/dev/project-generation/#what-is-gradle ↩︎
libGDX. (n.d.) Maven Integration. Retrieved March 28, 2021, from https://github.com/libgdx/libgdx/wiki/Maven-integration ↩︎
libGDX. (n.d.) Using libGDX with other JVM languages. Retrieved March 28, 2021, from https://github.com/libgdx/libgdx/wiki/Using-libGDX-with-other-JVM-languages ↩︎
libGDX. (n.d.) Managing your assets. Retrieved March 29, 2021, from https://github.com/libgdx/libgdx/wiki/Managing-your-assets ↩︎
libGDX. (n.d.) Internationalization and Localization. Retrieved March 29, 2021, from https://github.com/libgdx/libgdx/wiki/Internationalization-and-Localization ↩︎
libGDX. (n.d.) Coordinate systems. Retrieved March 29, 2021, from https://github.com/libgdx/libgdx/wiki/Coordinate-systems ↩︎
libGDX. (n.d.) File Handling. Retrieved March 29, 2021, from https://github.com/libgdx/libgdx/wiki/File-handling ↩︎
B. Dorland, M. P. H. Lips, L. Everse, C. Geukes. (2021) libGDX - Product Vision. Retrieved March 23, 2021, from https://2021.desosa.nl/projects/libgdx/posts/product-vision/ ↩︎
libGDX. (n.d.). HTML5 Backend and GWT Specifics. Retrieved March 28, 2021, from https://github.com/libgdx/libgdx/wiki/HTML5-Backend-and-GWT-Specifics ↩︎
B. Dorland, M. P. H. Lips, L. Everse, C. Geukes. (2021) libGDX - Quality and Evolution. Retrieved March 24, 2021, from https://2021.desosa.nl/projects/libgdx/posts/quality-and-evolution/ ↩︎
libgdx/libgdx. (n.d.-c). libgdx/tests at Master. Retrieved March 24, 2021, from https://github.com/libgdx/libgdx/tree/master/tests ↩︎
libgdx/libgdx. (n.d.-c). libgdx/backends at master. Retrieved March 24, 2021, from https://github.com/libgdx/libgdx/tree/master/backends ↩︎
B. Dorland, M. P. H. Lips, L. Everse, C. Geukes. (2021) libGDX - Architecture. Retrieved March 27, 2021, https://2021.desosa.nl/projects/libgdx/posts/architecture/ ↩︎
libGDX. (n.d.). The application framework. Retrieved March 28, 2021, from https://github.com/libgdx/libgdx/wiki/the-application-framework ↩︎
libgdx/libgdx (n.d.-c). libgdx/extensions.xml. Retrieved March 28, 2021, from https://github.com/libgdx/libgdx/blob/e0eebb2abef2db796ce7a093669029080330c125/extensions/gdx-setup/res/com/badlogic/gdx/setup/data/extensions.xml ↩︎