Exploring the archived APKs powering Android’s new app archiving feature
Since the start of the COVID-19 pandemic, I’ve only been in an Uber a little over a dozen times, and they were all during my first visit to Esper’s HQ in Bellevue a few months ago. Yet I still keep the Uber app installed on my phone because I’d hate having to sign into my account right when I need to call a ride. I have a bunch of other seldom-used apps on my phone that I just can’t get rid of, and I know I’m not alone. Wouldn’t it be neat if Android had iOS’s app offloading feature so I could free up the storage space taken by Uber and those other seldom-used apps on my phone without having to actually uninstall those apps? The good news is that Android is getting just that.
In a recent blog post, Google announced that it started working on a new feature that would enable “app archiving.” Archiving an app will let users reclaim ~60% of the storage space it took up by “removing parts of the app rather than uninstalling it completely.” Because the app is still technically installed on the device, the app’s data is preserved when archiving it, making it easy to get it back up and running.
Sounds great, right? Unfortunately, there’s a catch, albeit it’s a minor one. It requires a bit of support from app developers. Developers building their apps with the Android Gradle Plugin 7.3 will soon have a new type of APK called an “archived APK” generated for them. This “archived APK” will be generated through an updated version of bundletool, the tool that converts app bundles into the APKs that get deployed to devices. While Google says that it will start creating archived APKs now, it says those APKs won’t actually be functional until the archiving functionality launches to consumers later this year.
Google says that, instead of uninstalling an app, users will be able to “‘archive’ it”, implying it’s sticking a button somewhere to let users archive apps. The company really didn’t give many details on how the feature will appear on Android. Though since we’re waiting on the functionality to launch later this year, some took this to mean that app archiving will launch with Android 13.
Since the blog post was short on details and the feature has yet to launch, many people were wondering how app archiving will actually work. Fortunately, the current version of the open source bundletool already includes support for creating archived APKs, so I was able to play around with app archiving to get a sense of how it’ll work.
Thanks for signing up for The Android Edge newsletter. Every week, my Android Dessert Bites column will share the latest news about the Android platform that matters to system engineers, app developers, and power users.
App archiving, brought to you by the Android App Bundle
Before I dive into how app archiving will probably work, I need to give a primer on Android App Bundles for those who don’t know, because knowing how they work is important to understanding how archived APKs work.
Android apps are distributed inside of APKs, which are basically ZIP files with a special structure. Inside, they contain the app’s code, resources, libraries, some metadata, and other stuff. The size of the app depends on what’s inside the APK, and if there are a lot of large resources like images or audio, then the app could take up a lot of space on your device. Depending on how many display configurations and languages the developer wants to support, the APK may even include multiple versions of certain files. Distributing a single, universal APK that supports every density and language keeps things simple for developers, but it results in a lot of wasted space on users’ devices. That’s why Google made the Android App Bundle.
An Android App Bundle is a file that contains the app’s code and resources organized into modules. Developers upload an App Bundle to Google Play, which then uses those modules to generate only the APKs needed by a particular device. Google handles the distribution (and signing), so there’s little to no downside for most developers. App Bundles are required for all new apps on Google Play anyway, so it won’t be too difficult for many developers to support the new app archiving feature. Upgrading Android Studio’s build tools will be enough to support generating archived APKs, which is what I did to generate one for myself.
Generating an archived APK
When Google Play generates APKs from App Bundles, it makes those APKs available to developers through the Generated APKs API or the Play Console’s App Bundle Explorer. This is one way to grab the archived APK generated from an App Bundle, assuming Google Play has actually started creating them, of course. Another way to grab an archived APK is to generate it yourself through the command-line version of bundletool. I downloaded version 1.9.0 of bundletool from GitHub which is the latest version at the time of writing. Version 1.8.2 of bundletool also seems to support generating archived APKs (back when they were called “hibernated APKs” during development), but I haven’t tried that version yet.
Once you download bundletool, you can generate an archived APK by adding the –mode=archive flag to the build-apks command. For example:
bundletool build-apks --bundle=/MyApp/my_app.aab --mode=archive --output=/MyApp/my_app.apks
The archived APK can then be found under the “archive” folder in the generated .apks file.
If you get an error that states the archived APK “can only be generated for bundles built with version 1.8.2 or higher”, then you’ll need to upgrade your project’s Android Gradle Plugin.
You’ll also need to modify the build.gradle file to opt-in to generating an archived APK, otherwise you’ll see a message that the archived APK “cannot be generated when Store Archive configuration is disabled.” Google’s blog post tells you how to opt-out, so all you need to do is swap “false” for “true”.
What’s inside an archived APK?
An archived APK has the bare minimum needed for it to be recognized as a valid application. It has a Manifest with a small number of entries, a handful of resources, and code to handle what happens when the user launches it. Bundletool strips out pretty much everything else, leaving an incredibly tiny APK.
Let’s start with the Manifest of the archived APK. The Manifest will list the same version code, version name, and package name of the full app. Only APKs with the same or higher version and the same package name can be installed over the archived APK. Next, a metadata element named “com.android.vending.archive” identifies the app as an archived APK. Then there’s a single activity named “com.google.android.archive.ReactivateActivity” that’s launched when the user taps the home screen icon. Finally, there’s a single broadcast receiver called “com.google.android.archive.UpdateBroadcastReceiver” that listens for the “android.intent.action.MY_PACKAGE_REPLACED” intent, which is sent when the app is updated.
The resources pretty much just consist of the app’s icon as a drawable and the app’s name as a string.
When the user taps the archived APK’s icon, the first thing that happens in the ReactivateActivity is that an intent is sent targeting the package listed in STORE_PACKAGE_NAME. Bundletool makes this value “com.android.vending”, which is the package name for Google Play, but other app stores could modify bundletool to insert their own package name here. The app store needs to be able to respond to the intent and know that it came from an archived APK, though. In Google Play’s case, this is achieved by constructing the intent with the action “com.google.android.STORE_ARCHIVE.”
Since Google Play doesn’t respond to this intent yet, I don’t know for certain what’s supposed to happen, but it’s pretty obvious what should happen. Google Play should begin automatically downloading the APK splits needed to get the app up and running. Those APK splits can be installed over the archived APK because they’re all signed with the same app signing key and should have the same version code. Once those APKs are installed, the user will be right back where they left off because their data was never deleted when they archived the app.
In case of an error, the ReactivateActivity will throw up a dialog that tells the user that the “installation failed” and that the app “must be reinstalled from an official app store.” If the app store defined by STORE_PACKAGE_NAME is installed, then a “reinstall” button will appear in the dialog that, when tapped, takes the user to the app’s listing on the store page. Based on this error handling, it’s obvious what Google Play is supposed to do, but it’s not something that I can verify right now.
The other class — UpdateBroadcastReceiver — simply clears the archived app’s cache directory whenever it’s updated. I’m not entirely sure why this is needed, because it’d be risky to push an updated version of the full app while the data’s from an older version.
That’s all I’ve learned so far about Android’s app archiving feature from playing with the updated bundletool for a few hours. I don’t have any insight into how app archiving will be surfaced to users, but I’m guessing that Google Play will swap the “Uninstall” button for “Archive” for apps that have added support. Nothing that I’ve seen suggests this feature requires a specific Android version, especially not Android 13. In fact, I think app archiving may be available on older Android versions as well. It’s possible that the new Android version will add some kind of native settings integration with app archiving, but I haven’t seen any indication of that yet during my deep dive of Android 13.
Thanks for reading this week’s edition of Android Dessert Bites. If you liked this post, then please subscribe to The Android Edge newsletter to get links to future editions of my Android Dessert Bites column. You can find previous editions on this page.