Top 3 Best Room Heaters to Buy in 2020

Winter is here and the days are getting smaller, accompanied by chilly winds in the evenings. One of the most desirable appliance in such weather is a room heater. Room Heaters are available online…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Modularise your Android app with Dynamic Features

In the last few years, Google has never passed up a chance to convince developers to shrink their apps. Since the Google Play Store was introduced in 2012, Google has seen a massive increase in app size — the average size in 2019 was ~5x bigger than in 2012.

One may argue that app size is no longer a major concern with the widespread availability of unlimited mobile data plans and fibre optic broadband, but Google found a negative correlation between number of downloads and app size:

This figure starts to make sense when we take into account how fast the Android ecosystem is expanding in developing countries where more than 50% of Android users don’t have access to wifi and fast mobile data plans are still rarely seen.

When a user downloads an app using Dynamic Delivery, Google Play is able to deliver only the components that are strictly required to run on that specific device.

So before delivering an app to a device, Google Play strips out unnecessary languages, layouts for devices other than the one being used, parts compiled for different architectures, and features that are not immediately required.

With this approach, Dynamic Delivery can drastically reduce the download app size.

To take advantage of Dynamic Delivery, developers need to upload their apps in the new App Bundle (.aab) format.

APK delivery vs Dynamic Delivery with App Bundle

As mentioned above, with Dynamic Delivery Google Play can initially ship only some features of an app and defer the download of others for later. This capability is called Dynamic Features.

The undownloaded modules are delivered on-demand only when users really need them.

As a not-for-profit tech organisation, we operate in the poorest countries around the world, where we provide an Android app for doctors and health workers to identify people using their fingerprints (read more here).

In these countries, a reliable and fast connection is truly hard to find.

Average Download Speed by country — Speedtest.net

Simprints simply cannot ask doctors to download a 50 MB app when the clinic’s internet bandwidth is only a few KB/s, especially when vaccine delivery depends on whether our app can be successfully downloaded from the Google Play Store.

Below are a few examples of communications between the engineers from our office in Cambridge, UK and our project managers in the field.

As you can imagine, our engineering team welcomed Dynamic Features with joy and relief. Simply using the bundle format shrank our app from ~25MB to 6MB, which shortens the average download time from ~1h to ~15 minutes.

Bundles not only help our users and project managers in the field, but also unlock a world of new possibilities for our engineers. For years, we had often brainstormed potential features to improve our app, but always had to balance the benefits with the corresponding increase in APK size.

So after Google I/O 2018, we decided to start working on some of these features using Dynamic Feature Modules. After one year of working with this new feature, we wanted to share a few of the lessons we learned, particularly in the way they have impacted our approach to project structure, unit tests, and Android tests.

“App” is the main application module, which depends on the two other library modules. These are isolated entities that cannot have any references to App. The main advantage of this structure is that submodules are completely decoupled from the app.

With dynamic features, the dependency graph looks a bit different:

A dynamic feature module is a gradle module that represents a feature that may be unavailable at runtime, so the main App module needs to cope with the potential absence of a Feature module. To enforce this, Google decided to forbid any references in App to the Dynamic Feature module, so the following statement in App would throw a compiling issue:

import com.greatapp.featuremodule.class

But a dynamic feature module is not a library to be shared, but instead an intrinsic component (indeed a feature) of the main app. So a dynamic feature module can have references to the main app because if it gets shipped at runtime via the Google Play Store, surely the main app is installed on the device too.

So the dependency between App and a Dynamic Feature module is reversed compared to the relationship between App and library modules. Thus dynamic feature modules are not isolated in the same way traditional modules are. Keep this concept in mind, as we will return to it later.

App can interact with dynamic feature modules via Intents only:

One of the implications of dynamic feature modules not being isolated from the main App was that while our code was well-structured, we soon realised it was not scalable for our team. Our initial intention was to create dedicated teams that would develop and maintain feature modules autonomously.

To achieve this, we needed to avoid dynamic feature code being coupled with the app module. This could quickly lead to frustrating situations where dynamic feature tests fail due to changes in the app module, compiling issues are created by other teams, or where one team roadblocks others.

So we decided to introduce what we call a “moduleApi” module.

Moduleapi holds the API specifications for each module.

The specifications are represented by interfaces. Modules can only use these interfaces to interact with other modules, for all the rest they need to use domain classes.

In the previous example where App can launch a dynamic feature, we used the following line:

So app handles its business logic with domain classes and only when it’s time to launch the Intent, domain classes are transformed into classes that implements the moduleapi interfaces.

Correspondingly, the dynamic feature modules need to transform the classes extracted from Intents into domain classes:

So dynamic features can only import moduleapi interfaces. Thus, we don’t allow any references in the dynamic feature modules to code in the app module.

This is a convention that we defined in the Simprints team, but technically it’s still possible to have an illegal import referring to App in dynamic feature modules. To enforce our protocol, we take advantage of the “Dependencies Analysis”, which defines a specific scope for each feature module.

Once we started working with unit tests, our experience with dynamic features started becoming painful. We can run unit tests in Android Studio, but not from the command line or our CI system.

In general, it seems that Google introduced the dynamic features a bit too early, when the development tools were not ready for the new change. In line with our thinking, several issues were reported in the Android Studio issues tracker about dynamic features and unit tests:

The main issue is that classes imported from the App module are not found in the unit tests for the dynamic features.

As mentioned above, dynamic feature modules are not the same as library modules, especially when it comes to dependencies. Android Studio likely uses some trick to overcome this difference and run unit tests.

For us, being able to run unit tests in our CI was a vital requirement, so after struggling for days, we finally found an effective workaround.

In java-compiling terms, the “classes.jar” file of the main module was not included at compile time when unit tests are launched, so we simply included it:

It’s a hacky workaround, but it lets us compile and run our unit tests in our CI!

Android tests are another pain point we encountered. In this case, we had the opposite issue — we could launch Android tests from the command line with `./gradlew fingerprint:cAT`, but were unable to run them from Android Studio. Once again, the Android community was vocal in reporting this bug to Google:

Android Studio doesn’t recognise a dynamic feature module as a module that can run instrumented tests:

At Simprints, we try to cover as much code as possible with lean and fast unit tests and we use Android tests only for integration tests for UI, backend, and database.

So for us, not being able to run Android tests in Android Studio was not a big deal as long we could run in our CI. We use BitRise as our CI system and it runs Android tests on Firebase Test Lab (FTL). To run tests on FTL we need to upload 2 files: “App APK or AAB” and “Test APK”.

Then to build the “Test APK”, we ran:

dynamicFeatureModuleName:assembleDebugAndroidTest

Uploading the 2 APKs, we were finally able to run tests on FTL:

As a developer, the experience of working with dynamic features has not been as smooth as we would have liked, where we had to overcome poor documentation, development tools not yet ready for this new Android feature, and relatively low adoption from the Android community.

However, dynamic features definitely improve the final user experience. They help developers who need to ship features that require large files (such as AI models, 3D models, or game assets), which are becoming more and more common.

Add a comment

Related posts:

ARE HIERARCHIES REALLY BROKEN?

Existence is structural, in every form in whether inorganic or organic ways is to have where weight throws you. Hierarchical behaviorism in aspects of most disciplines like science, math…

Juan Manuel Santos tapa sus fraudes electorales atacando a Venezuela

El presidente colombiano Juan Manuel Santos ha decidido pronunciarse vía Twitter sobre las pasadas elecciones regionales del 15 de octubre, que concluyeron con -hasta ahora- 17 gobernaciones para el…

What To Do If Your Facebook Audience Speaks Multiple Languages

The world is full of rich, diverse cultures and beautiful languages. That is to our benefit. But as a business, what happens when you are fortunate enough to have an audience so diverse that they…