.NET for Android 9 allows you to avoid the 200 MB file size restrictions for your app on Google Play by using separate asset packs with 2 GB storage.
When publishing an app on Google Play, all the resources from your project, such as graphics, videos and audio, are packaged within the Android App Bundle (AAB), the file required to complete the app’s publication.
These resources are included in the main AAB package without issues, as long as they do not exceed 200 MB. If any file exceeds this limit, it cannot be included in the main package due to the size restrictions imposed by Google Play.
The good news is that, starting with .NET for Android 9, it is now possible to include resources in separate packages, known as asset packs. This allows you to upload additional asset packs of up to 2 GB, instead of being restricted to the 200 MB limit for the main package.
What key points should you keep in mind about the 2 GB limit?
This makes it easier to publish the app and offers more flexibility, so you don’t overload the main package while still complying with Google Play’s restrictions.
Basically, an asset pack is a collection of files containing your app’s resources.
⚠️ It is crucial to note that in .NET for Android, you must always set the Build Action property of each resource to “AndroidAsset” In contrast, when working with .NET MAUI, you should set it to “MauiAsset.” This sets the resources to be properly recognized as assets and included correctly in the asset packs.
To enable the use of asset packages in .NET for Android apps, the AndroidAsset item group now includes two new metadata attributes:
➖ AssetPack: This attribute allows us to specify which asset package the asset should be included in. The default value is the main package, so if no other value is specified, the resource will be included there.
The name of the asset package must be constructed as follows: $(AndroidPackage).%(AssetPack)
.
For instance, if your package name is com.mycompany.myproject
and the AssetPack name is myassets
, the resulting asset package name will be com.mycompany.myproject.myassets.
➖ DeliveryType: Determines when your assets are installed on the device. This property supports the following values:
Let’s put this into context. Imagine you have a file named Demo.mp4 that is over 200 MB. How can you move it to its own resource package? Here’s an example of how to do it in code:
<ItemGroup>
<AndroidAsset Update="Assets/Demo.mp4" AssetPack="myassets" />
</ItemGroup>
Let’s go over the details:
Inside the <ItemGroup> </ItemGroup>
tags, add the AndroidAsset
and specify the name of the AssetPack
. ItemGroup tells the .NET Android build system the following:
Update: The Update attribute is used instead of Include. If a resource is already present in the project and you only want to update its configuration (for example, assign it to an asset pack), use Update. If you’re adding a new resource, use Include.
Yes! 🌟 You can easily specify which resources stay in the main app bundle and which are moved to an asset pack. This is particularly useful when you have a large number of resources but only a few are essential to be available immediately upon installing the app.
Resources included in the main app bundle are delivered during the initial installation. In contrast, files in asset packs are downloaded based on the configured DeliveryType (for example, FastFollow, OnDemand, etc.).
Here’s a practical example:
<ItemGroup>
<AndroidAsset Update="Assets/*" AssetPack="myassets" />
<AndroidAsset Update="Assets/myfile.json" AssetPack="base" />
</ItemGroup>
Code Explanation:
<AndroidAsset Update="Assets/*" AssetPack="myassets" />
This specifies that all resources in the Assets folder will be placed in an asset pack named “myassets”.
<AndroidAsset Update="Assets/myfile.json" AssetPack="base" />
This indicates that the file myfile.json will be excluded from the myassets pack and placed in the main app bundle. This happens because the “base” value refers to the main package.
This approach gives you precise control over which resources are immediately available upon installation and which are delivered based on user needs.
If your app utilizes a FastFollow asset pack, you need to verify its status and that the pack is fully installed before attempting to access its content.
You can achieve this easily:
➖ Add the Xamarin.Google.Android.Play.Asset.Delivery package to your project via NuGet Package Manager.
This package provides access to the AssetPackManager type, enabling you to query the location of asset packs in your application.
Let’s look at a code example:
using Xamarin.Google.Android.Play.Core.AssetPacks;
var assetPackManager = AssetPackManagerFactory.GetInstance(this);
AssetPackLocation assetPackPath = assetPackManager.GetPackLocation("myfastfollowpack");
string assetsFolderPath = assetPackPath?.AssetsPath() ?? null;
if (assetsFolderPath is null)
{
// FastFollow asset pack isn't installed.
}
Here’s a review of what the code does:
If your app utilizes an OnDemand asset pack, you need to download it manually. To do this, simply add the same Xamarin.Google.Android.Play.Asset.Delivery NuGet package mentioned in the previous explanation.
Declare the necessary variables:
using Xamarin.Google.Android.Play.Core.AssetPacks;
IAssetPackManager assetPackManager;
AssetPackStateUpdateListenerWrapper listener;
Create the event handler to monitor the download progress:
void Listener_StateUpdate(object? sender, AssetPackStateUpdateListenerWrapper.AssetPackStateEventArgs e)
{
var status = e.State.Status();
switch (status)
{
case AssetPackStatus.Downloading:
long downloaded = e.State.BytesDownloaded();
long totalSize = e.State.TotalBytesToDownload();
double percent = 100.0 * downloaded / totalSize;
Android.Util.Log.Info("Download Progress", $"Downloading {percent}%");
break;
case AssetPackStatus.Completed:
Android.Util.Log.Info("Download Complete", "Asset pack download finished.");
break;
case AssetPackStatus.WaitingForWifi:
assetPackManager.ShowConfirmationDialog(this);
break;
}
}
Register the listener when starting and unregister it when pausing the application:
protected override void OnResume()
{
assetPackManager.RegisterListener(listener.Listener);
base.OnResume();
}
protected override void OnPause()
{
assetPackManager.UnregisterListener(listener.Listener);
base.OnPause();
}
Before attempting to download the asset pack, check its location with GetPackLocation
to determine if it is already installed.
var assetPackPath = assetPackManager.GetPackLocation("myondemandpack");
string assetsFolderPath = assetPackPath?.AssetsPath() ?? null;
if (assetsFolderPath is null)
{
// If it is not installed, start the download
await assetPackManager.Fetch(new string[] { "myondemandpack" }).AsAsync<AssetPackStates>();
}
Testing asset packs locally is straightforward; just follow these steps and keep these points in mind:
1️. Use the Android App Bundle (AAB) format. This is important because, by default, .NET for Android uses the Android Package (APK) format for debugging.
2️. Open your .csproj file and add the following debugging properties.
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<AndroidPackageFormat>aab</AndroidPackageFormat>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<AndroidBundleToolExtraArgs>--local-testing</AndroidBundleToolExtraArgs>
</PropertyGroup>
This code:
–local-testing
.IAssetPackManager
to use a mock downloader, which utilizes the cache. (This allows you to test the installation of OnDemand and FastFollow asset packs in a debugging environment.)This article was based on the official documentation and includes code examples from the main page to explain each implementation step in detail.
Leomaris Reyes is a Software Engineer from the Dominican Republic, with more than 5 years of experience. A Xamarin Certified Mobile Developer, she is also the founder of Stemelle, an entity that works with software developers, training and mentoring with a main goal of including women in Tech. Leomaris really loves learning new things! 💚💕 You can follow her: Twitter, LinkedIn , AskXammy and Medium.