Modern Android NDKTutorial

This short guide outlines the basics of utilizing the Android NDK to use C or C++ code in your Android App when using Android Studio 2.2+.

I had a bit of confusion when I started using the NDK and I’ve pieced together information I’ve learnt from various sources (a list of all sources is available at the bottom). This guide leans more towards C++ than C and covers:

  • Getting Started (Setting up the NDK)
  • Creating a Native method that can be invoked by Java
  • Passing Java objects between Java and Native Code
  • Passing an array of Java objects between Java and Native Code

Android Studio 2.2+ uses a CMake buildscript instead of ndk-build as was used previously. If you ever get stuck, then I recommend studing Google’s NDK sample code

Getting Started

Read Google’s “Getting Started with the NDK”

Their guide will provide you with a core set of information you need to use the Android NDK.

Add your C or C++ source files to your project

Create a CMake Build Script

Here is an example of a CMakeLists file. I am interested in having my Java code communicate with a C++ file named Myfile.cpp

# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.

cmake_minimum_required(VERSION 3.4.1)

# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add.library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.

add_library( # Specifies the name of the library. 
             # Sets the library as a shared library. 
             # Provides a relative path to your source file(s). 		
             src/main/cpp/Myfile.cpp )
# Add the directories where the Cpp header files are to let CMake find them 
# during compile time

has OpenGL Mathematics
as a dependency. I downloaded the glm C++ files I needed (I don’t believe you can use Gradle to manage C or C++ dependencies the same way you can Java) and stashed them at src/main/cpp/glm
, thus include_directories(src/main/cpp/)
is needed to include this dependency.

Link Gradle to your Native Library

You can specify optional arguments and flags for your CMake file. For example, MyFile.cpp
requires C++ 11 and must allow exceptions.

externalNativeBuild { 
  cmake { 
    	// Required by MyFile.cpp 
        cppFlags "-std=c++11" //Enable C++ 11 
        cppFlags "-fexceptions" //Allow exceptions 

Communicating between Java and Native Code

Now that you’ve set up the NDK, imported your Native (C/C++) code, and linked it to your Android project, next you need to enable your Java and Native Code to communicate with each other.

Java Side

I recommend creating a Java class to statically load your Native Library and declare your native methods that the Java Code that invoke.

// Wrapper for native library 
public class NativeLib {
  static { 
      // Replace "Myfile" with the name of your Native File 			 
  // Declare your native methods here 
    public static native String string();

Use System.loadLibrary("Myfile");
to load Myfile
(or whatever the Native file you want to interface with is called).

Native Side

  1. Create a Native Method that can be invoked by Java

You can’t just invoke any of your Native methods via your Java code, you need to implement Native methods that can be called by your Java code in a very specific way:

//You need this to allow methods that can be invoked by Java

//This syntax is needed if you are invoking a C++ method. C methods are slightly different

//Since the String method returns a String object, specify jstring. Replace this with whatever return object your method has.

extern "C" JNIEXPORT jstring JNICALL
//Replace the crazy long method name with the path to the Java file that is invoking it (com/example/NativeLib in this case).

//The Native Methods must always have JNIEnv *env, jobject object as the first two parameters
Java_com_example_NativeLib_string(JNIEnv *env, jobject object) { 
  //This is how you return a jstring via C++ code 
    return env->NewStringUTF("Hello from JNI LIBS!");

C Syntax vs. C++ Syntax

Original Post by Philippe Leefsma

// C syntax: my package name is com.autodesk.and.jnitester
// and my activity invoking the native method is MainActivity,
// hence the name of my method is
  JNIEnv *env, 
    jobject callingObject)
  return (*env)->NewStringUTF(env, "Native code rules!");

// C++ syntax: Required to declare as extern "C" to prevent c++ compiler
// to mangle function names
extern "C"
    JNIEnv *env, 
        jobject callingObject) 
    	return env->NewStringUTF("Native code rules!"); 
  1. Passing Objects between Java and Native Code

Original Post by Philippe Leefsma

First, define a simple POJO on the Java Side:

public class MeshData
  private int _facetCount; 
    public float[] VertexCoords; 
    public MeshData(int facetCount) 
    	_facetCount = facetCount; 
        VertexCoords = new float[facetCount];
        // fills up coords with dummy values
        for(int i=0; i<facetCount; ++i) { 
        	VertexCoords[i] = 10.0f * i; 
    public int getFacetCount() 
    	return _facetCount; 

Java method to pass your object to your Native Code:

public native float getMemberFieldFromNative(MeshData obj);

Reading a passed Java Object’s fields in Native Code:

  JNIEnv *env, 
    jobject callingObject, 
    jobject obj) //obj is the MeshData java object passed
  float result = 0.0f; 
    //Get the passed object's class 
    jclass cls = env->GetObjectClass(obj); 
    // get field [F = Array of floats 
    jfieldID fieldId = env->GetFieldID(cls, "VertexCoords", "[F"); 
    // Get the object field, returns JObject (because it’s an Array) 
    jobject objArray = env->GetObjectField (obj, fieldId); 
    // Cast it to a jfloatarray 
    jfloatArray* fArray = reinterpret_cast(&objArray); 
    jsize len = env->GetArrayLength(*fArray); 
    // Get the elements 
    float* data = env->GetFloatArrayElements(*fArray, 0); 
    for(int i=0; iReleaseFloatArrayElements(*fArray, data, 0); 
     return result;

Returning a value via a Native Function:

JNICALL Java_com_autodesk_adn_jnitester_MainActivity_invokeMemberFuncFromNative(
  JNIEnv *env, 
    jobject callingObject, 
    jobject obj)
  jclass cls = env->GetObjectClass(obj); 
    jmethodID methodId = env->GetMethodID(cls, "getFacetCount", "()I"); 
    int result = env->CallIntMethod(obj, methodId); 
    //Return the facet count (an int) 
    return result;

Instantiating a Java Object in Native Code:

  JNIEnv *env, 
    jobject callingObject, 
    jint param) 
    jclass cls = env->FindClass("com/autodesk/adn/jnitester/MeshData"); 

  jmethodID methodId = env->GetMethodID(cls, "", "(I)V"); 

  jobject obj = env->NewObject(cls, methodId, param); 
    return obj;
  1. Passing an Array of Objects between Java and Native Code

Java method to pass your array of objects to your Native Code:

You can’t pass a List or objects to your Native code, it needs to be an array.

public native int processObjectArrayFromNative(MeshData[] objArray);

Reading an array of Java Objects (a jobjectArray) in Native Code:

  JNIEnv *env, 
    jobject callingObject, 
    jobjectArray objArray)
  int resultSum = 0; 
    int len = env->GetArrayLength(objArray); 
    //Get all the objects in the array 
    for(int i=0; iGetObjectArrayElement(objArray, i); 
        resultSum += getFacetCount(env, obj); 
        return resultSum;

Return an array of objects using Native Code:

    jobject obj)


//Create a vector (an array) of Strings and add items to it vectorvec;







jobjectArray objarray = (env)->NewObjectArray(vec.size() ,clazz ,0);

for(int i = 0; i < vec.size(); i++) {

  string s = vec[i];
  (env)->SetObjectArrayElement(objarray , i , js);

return objarray;


稿源:Codementor Tutorials (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 移动开发 » Modern Android NDKTutorial

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录