Book Image

Mastering Android NDK

Book Image

Mastering Android NDK

Overview of this book

Android NDK is used for multimedia applications that require direct access to system resources. NDK is also the key for portability, which in turn allows a reasonably comfortable development and debugging process using familiar tools such as GCC and Clang toolchains. This is a hands-on guide to extending your game development skills with Android NDK. The book takes you through many clear, step-by-step example applications to help you further explore the features of Android NDK and some popular C++ libraries and boost your productivity by debugging the development process. Through the course of this book, you will learn how to write portable multi-threaded native code, use HTTP networking in C++, play audio files, use OpenGL ES 3, and render high-quality text. Each chapter aims to take you one step closer to building your application. By the end of this book, you will be able to create an engaging, complete gaming application.
Table of Contents (17 chapters)
Mastering Android NDK
Credits
About the Authors
About the Reviewers
www.PacktPub.com
Preface
Index

Embedding native code


Let's stick to the topic of this book and write some native C++ code for our template application. We will start with the jni/Wrappers.cpp file, which will contain a single function definition:

#include <stdlib.h>
#include <jni.h>
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "NDKApp", __VA_ARGS__))
extern "C"
{
  JNIEXPORT void JNICALL Java_com_packtpub_ndkmastering_AppActivity_onCreateNative( JNIEnv* env, jobject obj )
  {
    LOGI( "Hello Android NDK!" );
  }
}

This function will be called from Java using the JNI mechanism. Update AppActivity.java as follows:

package com.packtpub.ndkmastering;
import android.app.Activity;
import android.os.Bundle;
public class AppActivity extends Activity
{
  static
  {
    System.loadLibrary( "NativeLib" );
  }
  @Override protected void onCreate( Bundle icicle )
  {
    super.onCreate( icicle );
    onCreateNative();
  }
  public static native void onCreateNative();
};

Now, we have to build this code into an installable .apk package. We need a couple of configuration files for this. The first one, jni/Application.mk, contains the platform and toolchain information:

APP_OPTIM := release
APP_PLATFORM := android-19
APP_STL := gnustl_static
APP_CPPFLAGS += -frtti
APP_CPPFLAGS += -fexceptions
APP_CPPFLAGS += -DANDROID
APP_ABI := armeabi-v7a-hard
APP_MODULES := NativeLib
NDK_TOOLCHAIN_VERSION := clang

We use the latest version of the Clang compiler—that is 3.6, as we write these lines, and the armeabi-v7a-hard target, which enables support of hardware floating point computations and function arguments passing via hardware floating point registers resulting in a faster code.

The second configuration file is jni/Android.mk, and it specifies which .cpp files we want to compile and what compiler options should be there:

TARGET_PLATFORM := android-19
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := NativeLib
LOCAL_SRC_FILES += Wrappers.cpp
LOCAL_ARM_MODE := arm
COMMON_CFLAGS := -Werror -DANDROID -DDISABLE_IMPORTGL
ifeq ($(TARGET_ARCH),x86)
  LOCAL_CFLAGS := $(COMMON_CFLAGS)
else
  LOCAL_CFLAGS := -mfpu=vfp -mfloat-abi=hard -mhard-float -fno-short-enums -D_NDK_MATH_NO_SOFTFP=1 $(COMMON_CFLAGS)
endif
LOCAL_LDLIBS := -llog -lGLESv2 -Wl,-s
LOCAL_CPPFLAGS += -std=gnu++11
include $(BUILD_SHARED_LIBRARY)

Here, we link against OpenGL ES 2, specify compiler switches to enable the hardware floating point for non-x86 targets and list the required .cpp source files.

Use the following command from the root folder of the project to build the native code:

>ndk-build

The output should be as follows:

>ndk-build
[armeabi-v7a-hard] Compile++ arm  : NativeLib <= Wrappers.cpp
[armeabi-v7a-hard] SharedLibrary  : libNativeLib.so
[armeabi-v7a-hard] Install        : libNativeLib.so => libs/armeabi-v7a/libNativeLib.so

The last thing is to tell Gradle that we want to pack the resulting native library into the .apk. Edit the build.gradle file and add the following line to the main section of sourceSets:

jniLibs.srcDirs = ['libs']

Now, if we run the command gradle build, the resulting package build\outputs\apk\3_NDK-debug.apk will contain the required libNativeLib.so file. You can install and run it as usual. Check the line Hello Android NDK! printed into the Android system log with adb logcat.

Note

Those who do not want to tackle Gradle in such a small project without dependencies will be able to use good old Apache Ant. Just run the command ant debug to make it happen. No additional configuration files are required to put shared C++ libraries into .apk this way.