Starting August 1, 2019, your apps published on Google Play will need to support 64-bit architectures. 64-bit CPUs deliver faster, richer experiences for your users. Adding a 64-bit version of your app provides performance improvements, makes way for future innovation, and sets you up for devices with 64-bit-only hardware.

This guide explains the steps you can take today in order to ensure that your 32-bit app is ready to support 64-bit devices.

Assess your app

If your app uses only code written in the Java programming language or Kotlin, including all libraries or SDKs, your app is already ready for 64-bit devices. If your app uses any native code, or you are unsure if it does, you will need to assess your app and take action.

Quick status check

A quick way to check whether your app is ready for the 64-bit requirement is to go to the Play Console and take a look at existing releases to see whether they are compliant:

Play Console will also show warnings that apply to your draft releases, if there are any issues related to the 64-bit requirement. Here's an example:

If you see an alert, follow the steps below to get your app ready.

Does your app use native code?

The first thing to do is check to see if your app uses any native code. Your app makes use of native code if it:

Uses any C/C++ (native) code in your app.

Links with any third party native libraries.

Is built by a third-party app builder that uses native libraries.

Does your app include 64-bit libraries?

The simplest way to check for 64-bit libraries is to inspect the structure of your APK file. When built, the APK will be packaged with any native libraries needed by the app. Native libraries are stored in various folders based on the ABI. It isn't required to support every 64-bit architecture, but for each native 32-bit architecture you support you must include the corresponding 64-bit architecture.

For the ARM architecture, the 32-bit libraries are located in armeabi-v7a. The 64-bit equivalent is arm64-v8a.

For the x86 architecture, look for x86 for 32-bit and x86_64 for 64-bit.

The first thing to do is ensure that you have native libraries in both of these folders. To recap:

Platform 32-bit libraries folder 64-bit libraries folder ARM lib/armeabi-v7a lib/arm64-v8a x86 lib/x86 lib/x86_64

Note that depending on your app, there may or may not be exactly the same set of libraries in each folder. The goal is to ensure that your app runs correctly in a 64-bit-only environment.

In a typical case, an APK or bundle that's built for both 32-bit and 64-bit architectures will have folders for both ABIs, each with a corresponding set of native libraries. If there's no support for 64-bit, you'll likely see a 32-bit ABI folder but not a 64-bit folder.

Look for native libraries using APK Analyzer

APK Analyzer is a tool that allows you to evaluate various aspects of a built APK. In our case, we're going to use it to find any native libraries, and ensure 64-bit libraries are present.

Open Android Studio, and open any project. From the menu, select Build > Analyze APK… Choose the APK you wish to evaluate. Look within the lib folder, which is where you will find any '.so' files. If you can not find any '.so' files in your app at all, then your app is already ready and no further action is required. If you see armeabi-v7a or x86, then you have 32-bit libraries. Check to see if you have similar '.so' files in the arm64-v8a or x86_64 folder. If you do not have any arm64-v8a or x86_64 libraries, you'll need to update your build process to start building and packaging those artifacts in your APK. If you already see both libraries being packaged, you can skip ahead to Test Your app on a 64-bit device.

Look for native libraries by unzipping APKs

APK files are structured like zip files, and can be extracted like them as well. If you prefer to use the command line or any other extraction tool, unzipping the APK will work for you.

Simply unzip the APK file (depending on your extraction tool, you might have to rename it to .zip) and browse the files that are extracted, following the guidance above to find out if you are ready for 64-bit devices.

For example, you can run the following command from the command line:

:: Command Line > zipinfo -1 YOUR_APK_FILE.apk | grep \.so$ lib/armeabi-v7a/libmain.so lib/armeabi-v7a/libmono.so lib/armeabi-v7a/libunity.so lib/arm64-v8a/libmain.so lib/arm64-v8a/libmono.so lib/arm64-v8a/libunity.so

Note in this example the presence of armeabi-v7a and arm64-v8a libraries, which means the app supports 64-bit architectures.

Build your app with 64-bit libraries

What follows are instructions to build 64-bit libraries. However, you should understand that this will only cover building code and libraries that you are able to build from source.

If you are using any external SDKs or libraries, ensure you are using 64-bit versions by following the steps above. Reach out to the SDK or library owner if a 64-bit version is not available and take this into account when planning your support for 64-bit devices.

Building with Android Studio or Gradle

Most Android Studio projects use Gradle as the underlying build system, so this section applies to both cases. Enabling builds for your native code is as simple as adding the arm64-v8a and/or x86_64, depending on the architecture(s) you wish to support, to the ndk.abiFilters setting in your app's 'build.gradle' file:

// Your app's build.gradle apply plugin: 'com.android.app' android { compileSdkVersion 27 defaultConfig { appId "com.google.example.64bit" minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" ndk.abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64' // ...

Building with CMake

If your app is built using CMake, you can build for 64-bit ABIs by passing the arm64-v8a into the '-DANDROID_ABI' parameter:

:: Command Line > cmake -DANDROID_ABI=arm64-v8a … or > cmake -DANDROID_ABI=x86_64 …

This option has no effect when using externalNativeBuild . See the Building with Gradle section.

Building with ndk-build

If your app is build with ndk-build, you can build for 64-bit ABIs by modifying your 'Application.mk' file using the APP_ABI variable:

APP_ABI := armeabi-v7a arm64-v8a x86 x86_64

This option has no effect when using externalNativeBuild . See the Building with Gradle section.

Porting 32-bit code to 64-bit

If your code already runs on the desktop or iOS, you shouldn't need to do any extra work for Android. If this is the first time your code has been built for a 64-bit system, the main issue you will need to address is that pointers no longer fit in 32-bit integer types like int . You will need to update code that stores pointers in types such as int , unsigned , or uint32_t . On Unix systems, long matches the pointer size, but this isn't true on Windows, so you should use the intention-revealing types uintptr_t or intptr_t instead. Use the ptrdiff_t type to store the difference between two pointers.

You should always prefer the specific fixed width integer types defined in <stdint.h> rather than traditional types such as int or long , even for non-pointers.

Use the following compiler flags to catch cases where your code is incorrectly converting between pointers and integers:

-Werror=pointer-to-int-cast -Werror=int-to-pointer-cast -Werror=shorten-64-to-32

Java classes with int fields that hold pointers to C/C++ objects have the same problem. Search for jint in your JNI source and ensure that you switch to long on the Java side and jlong on the C++ side.

Note: Crashes that stem from pointers being truncated will manifest as a SIGSEGV where the top 32 bits of the fault address are all zero.

Implicit function declarations are a lot more dangerous for 64-bit code. C/C++ assume that the return type of an implicitly declared function (that is, a function that the compiler hasn't seen a declaration for) is int . If the actual return type of your function is a pointer, this works fine on a 32-bit system where your pointer fits into an int , but on a 64-bit system the compiler will drop the top half of your pointer. For example:

// This function returns a pointer: // extern char* foo(); // If you don't include a header that declares it, // when the compiler sees this: char* result = foo(); // Instead of compiling that to: result = foo(); // It compiles to something equivalent to: result = foo() & 0xffffffff; // Which will then cause a SIGSEGV if you try to dereference `result`.

The following compiler flag turns implicit function declaration warnings into errors so that you can find and fix this problem more easily:

-Werror=implicit-function-declaration

If you have inline assembler, you'll need to rewrite it or use a plain C/C++ implementation.

If you have hard-coded sizes of types (8 or 16 bytes, for example), replace them with the equivalent sizeof(T) expression, such as sizeof(void*) .

If you need to conditionally compile different code for 32-bit than 64-bit, you can use the #if defined(__LP64__) for generic 32/64 differences, or __arm__ , __aarch64__ (arm64), __i386__ (x86), and __x86_64__ for the specific architectures supported by Android.

You will need to adjust format strings for printf or scanf -like functions, as the traditional format specifiers don't allow you to specify 64-bit types in a way that's correct for both 32-bit and 64-bit devices. The PRI and SCN macros in <inttypes.h> solve this problem, PRIxPTR and SCNxPTR for writing/reading hex pointers, and PRId64 and SCNd64 for writing/reading 64-bit values portably.

When shifting, you may need to use 1ULL to get a 64-bit constant to shift rather than using 1 , which is only 32 bits.

Mitigating size increases with Android App Bundle

Adding 64-bit architecture support to your app can cause your APK size to grow. We strongly recommend taking advantage of the Android App Bundle feature to minimize the size impact of including both 32- and 64-bit native code in the same APK.

Switching your app to using Android App Bundles can actually be an improvement to your APK size, reducing it to less than what it is right now.

Game developers

We understand that migrating a third-party game engine is an intensive process with long lead times. Thankfully, the three most used engines all currently support 64-bit:

Unreal since 2015

Cocos2d since 2015

Unity since 2018

Unity developers

Upgrade to capable versions

Unity began providing 64-bit support with versions 2018.2 & 2017.4.16.

If you find you are on a version of Unity that does not support 64-bit, determine the version you wish to upgrade to and follow the guides that Unity provides to migrate your environment, ensuring your app is upgraded to a version that can build 64-bit libraries. Unity recommends you have access to the latest features and updates by upgrading to the latest LTS version of the editor.

Here's a chart that outlines the various Unity versions and what you should do:

Unity Version Version supports 64-bit? Recommended course of action 2020.x ✔️ Ensure your build settings output 64-bit libraries. 2019.x ✔️ Ensure your build settings output 64-bit libraries. 2018.4 (LTS) ✔️ Ensure your build settings output 64-bit libraries. 2018.3 ✔️ Ensure your build settings output 64-bit libraries. 2018.2 ✔️ Ensure your build settings output 64-bit libraries. 2018.1 ➖ Has experimental 64-bit support. 2017.4 (LTS) ✔️ Supported as of 2017.4.16. Ensure your build settings output 64-bit libraries. 2017.3 ✖️ Upgrade to version that supports 64-bit. 2017.2 ✖️ Upgrade to version that supports 64-bit. 2017.1 ✖️ Upgrade to version that supports 64-bit. <=5.6 ✖️ Upgrade to version that supports 64-bit.

Change build settings to output 64-bit libraries

If you are using a version of Unity that supports 64-bit Android libraries, you can generate a 64-bit version of your app by adjusting your build settings. You will also need to use the IL2CPP backend as your Scripting Backend (details here). To set up your Unity project to build 64-bit architecture, do the following:

Go to Build Settings and ensure you are building for Android by verifying that the Unity symbol is next to Android under Platform. If the Unity symbol is not next to the Android platform, select Android and click Switch Platform. Click Player settings. Navigate to Player Settings Panel > Settings for Android > Other settings > Configuration Set Scripting Backend to IL2CPP. Select the Target Architecture > ARM64 checkbox. Build as normal!

Note that building for ARM64 will require all your assets to be built specifically for that platform. Follow Unity's guidance for reducing APK size, and consider taking advantage of the Android App Bundle feature to help mitigate this increase in size.

Multi-APK and 64-bit compliance

If you are using Google Play’s multiple-APK support to publish your app, note that compliance with the 64-bit requirement is evaluated at the release level. However, the 64-bit requirement does not apply to APKs or app bundles that are not distributed to devices running Android 9 Pie or later.

If one of your APKs is marked as not being compliant, but is older and it’s not possible to bring it into compliance, one strategy is to add a maxSdkVersion="27" attribute in the uses-sdk element in that APK’s manifest. This APK won’t be delivered to devices running Android 9 Pie or later, and it will no longer block compliance.

RenderScript and 64-bit compliance

If your app uses RenderScript and was built with an older version of the Android tools, you might see 64-bit compliance issues for the app. With build tools earlier than 21.0.0, the compiler may generate bitcode into an external .bc file. These legacy .bc files are no longer supported for 64-bit architectures, so the presence of the file in your APK causes the compliance issue.

To fix the issue, remove any .bc files in your project, upgrade your environment to build-tools-21.0.0 or later, and set the renderscriptTargetApi in Android Studio to 21+, to tell the compiler to not emit .bc files. Then, rebuild your app, inspect for .bc files, and upload to Play Console.

Test your app on 64-bit hardware

The 64-bit version of your app should offer the same quality and feature set as the 32-bit version. Test your app to make sure that users on the latest 64-bit devices have a great experience in your app.

In order to start testing your app, you will need to have a 64-bit-capable device. There are a variety of popular devices available that are 64-bit capable, such as Google's Pixel and other flagship devices.

The easiest way to test your APK is to install the app using adb. In most cases, you can supply --abi as a parameter to indicate which libraries to install to the device. This will install the app with only the 64-bit libraries on the device.

:: Command Line # A successful install: > adb install --abi armeabi-v7a YOUR_APK_FILE.apk Success # If your APK does not have the 64-bit libraries: > adb install --abi arm64-v8a YOUR_APK_FILE.apk adb: failed to install YOUR_APK_FILE.apk: Failure [INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113] # If your device does not support 64-bit, an emulator, for example: > adb install --abi arm64-v8a YOUR_APK_FILE.apk ABI arm64-v8a not supported on this device

Once you have installed successfully, test your app like you normally would to ensure the quality is the same as the 32-bit version.

Publish

When you feel like your app is ready, publish as normal. As always, continue to follow the best practices for deploying your app. We recommend taking advantage of closed testing tracks to rollout to a limited number of users to ensure the quality of your app is consistent.

As when rolling out an major update, make sure you have thoroughly tested on 64-bit-capable devices before publishing to a larger audience.

Related