Building ABI-Specific APKs
This document will discuss how to build an APK that will target a single ABI using Xamarin.Android.
Overview
In some situations it may be advantageous for an application to have multiple APKs - each APK is signed with the same keystore and shares the same package name but it is compiled for a specific device or Android configuration. This is not the recommended approach - it is much simpler to have one APK that can support multiple devices and configurations. There are some situations where creating multiple APKs can be useful, such as:
Reduce the size of the APK - Google Play imposes a 100MB size limit on APK files. Creating device specific APK's can reduce the size of the APK as you only need to supply a subset of assets and resources for the application.
Support different CPU architectures - If your application has shared libraries for specific CPU's, you can distribute only the shared libraries for that CPU.
Multiple APKs can complicate distribution - a problem that is addressed by Google Play. Google Play will ensure that the correct APK is delivered to a device based on the application's version code and other metadata contained with AndroidManifest.XML. For specific details and restrictions on how Google Play supports multiple APKs for an application, consult Google's documentation on multiple APK support.
This guide will address how to script the building multiple APKs for a Xamarin.Android application, each APK targeting a specific ABI. It will cover the following topics:
- Create a unique version code for the APK.
- Create a temporary version of AndroidManifest.XML that will be used for this APK.
- Build the application using the AndroidManifest.XML from the previous step.
- Prepare the APK for release by signing and zip-aligning it.
At the end of this guide is a walkthrough that will demonstrate how to script these steps using Rake.
Creating the Version Code for the APK
Google recommends a particular algorithm for the version code that uses a seven digit version code (please see the section Using a version code scheme in the Multiple APK support document). By expanding this version code scheme to eight digits, it is possible to include some ABI information into the version code that will ensure that Google Play will distribute the correct APK to a device. The following list explains this eight digit version code format (indexed from left to right):
Index 0 (red in diagram below) – An integer for the ABI:
- 1 –
armeabi
- 2 –
armeabi-v7a
- 6 –
x86
- 1 –
Index 1-2 (orange in diagram below) – The minimum API level supported by the application.
Index 3-4 (blue in diagram below) – The screen sizes supported:
- 1 – small
- 2 – normal
- 3 – large
- 4 – xlarge
Index 5-7 (green in diagram below) – A unique number for the version code. This is set by the developer. It should increase for each public release of the application.
The following diagram illustrates the position of each code described in the above list:
Google Play will ensure that the correct APK is delivered to the device
based on the versionCode
and APK configuration. The APK with the
highest version code will be delivered to the device. As an example, an
application could have three APKs with the following version codes:
- 11413456 - The ABI is
armeabi
; targeting API level 14; small to large screens; with a version number of 456. - 21423456 - The ABI is
armeabi-v7a
; targeting API level 14; normal & large screens; with a version number of 456. - 61423456 - The ABI is
x86
; targeting API level 14; normal & large screens; with a version number of 456.
To continue on with this example, imagine that a bug was fixed which
was specific to armeabi-v7a
. The app version increases to 457, and an
new APK is built with the android:versionCode
set to 21423457. The
versionCodes for the armeabi
and x86
versions would remain the
same.
Now imagine that the x86 version receives some updates or bug fixes
that target a newer API (API level 19), making this version 500 of the
app. The new versionCode
would change to 61923500 while the
armeabi/armeabi-v7a remain unchanged. At this point in time, the
version codes would be:
- 11413456 - The ABI is
armeabi
; targeting API level 14; small to large screens; with a version name of 456. - 21423457 - The ABI is
armeabi-v7a
; targeting API level 14; normal & large screens; with a version name of 457. - 61923500 - The ABI is
x86
; targeting API level 19; normal & large screens; with a version name of 500.
Maintaining these version codes manually can be a significant burden on
the developer. The process of calculating the correct
android:versionCode
and then building the APK's should be automated.
An example of how to do so will be covered in the walkthrough at the
end of this document.
Create A Temporary AndroidManifest.XML
Although not strictly necessary, creating an temporary
AndroidManifest.XML for each ABI can help prevent issues that might
arise with information leaking from one APK to the other. For example,
it is crucial that the android:versionCode
attribute is unique for
each APK.
How this is done depends on the scripting system involved, but typically involves taking a copy of the Android manifest used during development, modifying it, and then using that modify manifest during the build process.
Compiling the APK
Building the APK per ABI is best accomplished by using either xbuild
or msbuild
as shown in the following sample command line:
/Library/Frameworks/Mono.framework/Commands/xbuild /t:Package /p:AndroidSupportedAbis=<TARGET_ABI> /p:IntermediateOutputPath=obj.<TARGET_ABI>/ /p:AndroidManifest=<PATH_TO_ANDROIDMANIFEST.XML> /p:OutputPath=bin.<TARGET_ABI> /p:Configuration=Release <CSPROJ FILE>
The following list explains each command line parameter:
/t:Package
– Creates an Android APK that is signed using the debug keystore/p:AndroidSupportedAbis=<TARGET_ABI>
– This the ABI to target. Must one ofarmeabi
,armeabi-v7a
, orx86
/p:IntermediateOutputPath=obj.<TARGET_ABI>/
– This is the directory that will hold the intermediate files that are created as a part of the build. If necessary, Xamarin.Android will create a directory named after the ABI, such asobj.armeabi-v7a
. It is recommended to use one folder for each ABI as this will prevent issues that make result with files "leaking" from one build to another. Notice that this value is terminated with a directory separator (a/
in the case of OS X)./p:AndroidManifest
– This property specifies the path to the AndroidManifest.XML file that will be used during the build./p:OutputPath=bin.<TARGET_ABI>
– This is the directory that will house the final APK. Xamarin.Android will create a directory named after the ABI, for examplebin.armeabi-v7a
./p:Configuration=Release
– Perform a Release build of the APK. Debug builds may not be uploaded to Google Play.<CS_PROJ FILE>
– This is the path to the.csproj
file for the Xamarin.Android project.
Sign and Zipalign The APK
It is necessary to sign the APK before it can be distributed via Google
Play. This can be performed by using the jarsigner
application that
is a part of the Java Developer's Kit. The following command line
demonstrats how to use jarsigner
at the command line:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore <PATH/TO/KEYSTORE> -storepass <PASSWORD> -signedjar <PATH/FOR/SIGNED_JAR> <PATH/FOR/JAR/TO/SIGN> <NAME_OF_KEY_IN_KEYSTORE>
All Xamarin.Android applications must be zip-aligned before they can be run on a device. This is the format of the command line to use:
zipalign -f -v 4 <SIGNED_APK_TO_ZIPALIGN> <PATH/TO/ZIP_ALIGNED.APK>
Automating APK Creation With Rake
The sample project OneABIPerAPK is a simple Android project that will demonstrate how to calculate an ABI specific version number and build three separate APK's for each of the following ABI's:
- armeabi
- armeabi-v7a
- x86
The rakefile in the sample project performs each of the steps that were described in the previous sections:
Create an android:versionCode for the APK.
Write the android:versionCode to a custom AndroidManifest.XML for that APK.
Compile a release build of the Xamarin.Android project that will singularly target the ABI and using the AndroidManifest.XML that was created in the previous step.
Sign the APK with a production keystore.
Zipalign the APK.
To build all of the APKs for the application, run the build
Rake task
from the command line:
$ rake build
==> Building an APK for ABI armeabi with ./Properties/AndroidManifest.xml.armeabi, android:versionCode = 10814120.
==> Building an APK for ABI x86 with ./Properties/AndroidManifest.xml.x86, android:versionCode = 60814120.
==> Building an APK for ABI armeabi-v7a with ./Properties/AndroidManifest.xml.armeabi-v7a, android:versionCode = 20814120.
Once the rake task has completed, there will be three bin
folders
with the file xamarin.helloworld.apk
. The next screenshot shows each
of these folders with their contents:
Note
The build process outlined in this guide may be implemented in one of many different build systems. Although we don't have a pre-written example, it should also be possible with Powershell / psake or Fake.
Summary
This guide provided some suggestions with how to create Android APK's
that target a specify ABI. It also discussed one possible scheme for
creating android:versionCodes
that will identify the CPU architecture
that the APK is intended for. The walkthrough included a sample project
that has it's build scripted using Rake.