Cyril Mottier

“It’s the little details that are vital. Little things make big things happen.” – John Wooden

System-wide Library Sharing

It is no secret I have always had interest in making mobile apps as lightweight as possible. Back in 2014, I wrote “Putting Your APKs on Diet” explaining in details the anatomy of an APK and giving several tips and tricks Android developers can use to reduce the binary size of an APK.

We’re now in 2017 and mobile apps are bigger than ever. According to Sensor Tower’s analysis of App Intelligence1, the total space required by the top 10 most installed iPhone apps in the U.S. has grown from 164 MB in May 2013 to about 1.9 GB in May 2017, a 12x increase in just four years. Unfortunately, this analysis only focuses on iPhone apps but, from my experience, Android apps has also increased in size in the past four years.

One might say, app file size increase is actually completely normal. Indeed, a lot of things changed in the past 4 years and users expectations increased too: devices have higher densities, apps bundle more features and provide richer experiences, etc. As a consequence, the question is not “Is the increase in file size normal?” but rather “Is the increase in file size smaller/larger than it should”. In other words, does the increase worth the value? The answer to that question is rather complex and subjective.

A lot of things have changed in 4 years to reduce the amount of data transmitted over the air when delivering applications as well as the disk space occupied on the device.

  • Dead code elimination (i.e. Proguard)
  • Removal of unused resources (e.g. shrinkResources)
  • Vector graphic assets
  • Downloadable fonts
  • APKs splits / App Thinning
  • Versioned resources collapsing and resources deduplication (with AAPT v2)
  • Sparse translations elimination (with resConfigs)
  • Binary diff on update
  • etc.2

Today, the biggest issue when looking at the size of an app is generally the large amount of dependencies your application relies on. Indeed, dependencies come with resources (images, sounds, etc.), native code (.so) and so on. In particular, support libraries like support-v4 or appcompat are now bundled in almost all apps in the Play Store and comes with tons of resources or code you might not be using. Even worse, this code gets duplicated in all apps.

Android developers are so used to support libraries, they probably don’t even remember how large an Android application should be. Here is what you get when creating two helloworld apps (one with appcompat and one without it) and building it with minify and resources shrinking enabled:

The helloworld binary built with appcompat is around 621KB while the one without appcompat is only 3KB (yep you read it correctly… only 3KB). One could expect both Proguard and the shrinker to get rid of the unused code and resources. Unfortunately, this is not possible. Both of these tools look at the code dependency graph at compile-time but cannot know exactly whether or not a snippet of code will be executed at runtime.

Support libraries are first-citizens in Android development and nobody can imagine developing an Android app today without them. As a consequence, getting rid of them is obviously not a solution. A possible solution would be to have something very similar to what has been done recently with downloadable fonts: download the required libraries (at the appropriate version) at the system level and share them between installed applications.

It looks like Google is thinking about such a concept as pointed in a talk about Instant Apps at Google I/O:

We’re focused on features that can enable additional binary size reduction […] Allowing commonly used libraries like appcompat to be shared between Instant Apps

Obviously, only Instant Apps are mentioned in this talk but I don’t see a reason why it couldn’t apply to installable Android apps as well.

Some other recent changes in the Android ecosystem might act as some of the first steps towards such a system-wide library sharing:

  • The introduction of a Maven repository owned by Google at maven.google.com (aka google() in your repositories DSL of your build.gradle files). This could act as the central repository where the system would download missing libraries. That would also suggest only “Google-provided” libraries (e.g. support librairies, Play Services, etc.) could be eligible to library sharing.
  • The addition of several <meta-data /> block in your app manifests indicating the versions of the libraries your application is based on. This could clearly inform the system which libraries should be pre-fetched at installation-time.

Google hasn’t shared documentation on this yet so everything I described here might not exist. Knowing how this would drastically reduce applications binary size, I’m really looking forward to see whether or not system-wide library sharing will become a reality.


1: You can learn more about this analysis reading this blog post: The Size of iPhone’s Top Apps Has Increased by 1,000% in Four Years

2: I really encourage you to discover more about how to slim down your app size watching this Google I/O session.