Using assembly in Android Studio

There are many reasons why to write your algorithm in assembler. You can have all the optimizations firmly in hand. However, it comes at costs – you need to know a lot about the hardware running your program, your program will be highly non-portable, non readable and you will spend ages on writing it. Oh, did I mention that debugging is a hell?

Preparation

So, to start using assembly in Android Studio, you need to install Android NDK. You can install it manually (and reference the installation) or from the Android Studio itself ( File > Settings > Appearence & Behaviour > System Settings > Android SDK > SDK Tools ).

Check your Android NDK installation path. In case you installed in from Android studio, it will most likely be located at path:

$(ANDROID_SDK)\ndk-bundle

You are going to need the location to run some commands.

The good thing is that the android NDK comes with all necessary tools, like the assembly compiler and cmake, built-in – so you don’t have to install anything else.

We are going to make a simple implementation of the sum function in assembly, and we are going to target only one platform (armeabi-v7a).

Writing your first assembly app

Here are the individual steps:

  • Create an empty project, and name it AsmSum.  The important step it to check Include C++ support. You can select the desired platform and an empty activity. For the purpose of the example, we assume a phone or a tablet app based on armeabi-v7a.
  • Open your application gradle file and add abiFilter to your ndk settings and specify your compiler. Since the instruction set varies among different processors and platform, in this tutorial we focus on one platform, being armeabi-v7a and compiler gcc:
android {
    defaultConfig {
        ...
        ndk {
            abiFilters 'armeabi-v7a'
        }
        externalNativeBuild {
            cmake {
                arguments '-DANDROID_TOOLCHAIN=gcc'
            }
        }
        ...
    }
    ...
}
  • Now we need to add/create the assembly file.  Create a file in the path $(PROJECT)/src/main/cpp/multiply.s. This file will not be visible through the Android studio, unfortunately, it does not support assembly files yet (my best guess is it never will).
  @ This file is multiply.s
  .text
  .align  2
  .global  multiply
  .type  multiply, %function
multiply:
  @ Multiply the arguments in registers r0 and r1
  stmfd  sp!, {fp,ip,lr}
  mul  r0, r1, r0
  ldmfd  sp!, {fp,ip,lr}
  bx  lr
  .size  multiply, .-multiply
  • Even though the assembly file multiply.s is not visible in the Android Studio, it is there and we need to instruct the building script to use it. For practical reasons, I recommend to create a separate native (static) library with your assembly code – by doing that you can easily separate code for different instruction sets. To create a separate library that will contain our multiply.s, add following lines to your cmake file CMakeList.txt.
set(can_use_assembler TRUE)
enable_language(ASM)

add_definitions(-DANDROID -DOC_ARM_ASM)

add_library( multiply-lib STATIC
             src/main/cpp/multiply-lib.s )
  • … and of course, link it to the final library native-lib that will be installed with the app:
target_link_libraries( native-lib
                       multiply-lib
                       ${log-lib} )
  • The last task is to modify the C++ file with our JNI function to demonstrate the functionality of our assembly function. Since we created our assembly function in a C-like manner, we need to declare it to use it in a C++ files. In case you have more functions like this it might be handy to put these definitions into a separate header file.
#include <jni.h>
#include <string>
#include <android/log.h>

extern "C" {
    jint multiply(jint a, jint b);
}

extern "C" JNIEXPORT jstring

JNICALL
Java_com_omelina_assembly_asmsum_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {

    __android_log_print(ANDROID_LOG_DEBUG,"NATIVE","%d ", multiply(6,7));

    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
  • Tada … after compiling and running the project on a device you should see a log in the logcat window:
...
10-21 23:13:22.374 29816-29816/com.omelina.assembly.asmsum D/NATIVE: 42
...

That is it. You just created a simple project demonstrating how to use assembly in an Android Studio project. The full source code can be found here.