Android NDKFederico Menozzi & Srihari Pratapa
Resources● C++
○ http://www.cplusplus.com/doc/tutorial/○ https://en.wikipedia.org/wiki/Comparison_of_Java_and_C%2B%2B
● CMake○ https://cmake.org/cmake-tutorial/○ http://mathnathan.com/2010/07/getting-started-with-cmake/
● NDK○ https://developer.android.com/ndk/index.html
● JNI○ https://developer.android.com/training/articles/perf-jni.html○ https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html (optional)
Android Stack
NDK? JNI? What?● NDK
○ Native Development Kit○ Allows developers to create Android applications with C/C++○ Uses JNI to allow for Java/native interop
● JNI○ Java Native Interface○ Allows for Java to call native (i.e. C/C++) code and vice-versa○ Used as a bridge between Android system and native code
What is C++?● High-performance systems-level programming language● Supports imperative, object-oriented, and functional programming patterns● Created to be a (nearly) strict superset of C
○ This means most C programs are also valid C++ programs
Who Cares About C++?● Literally everyone● People who care about performance● People who care about abstraction● Masochists● Used to create operating systems, web browsers, compilers/interpreters,
network stacks, database, graphics/game engines, etc.
C++ for Idiots Java Programmers
A Quick Word About C++...● Fiendishly complicated● Outrageously complex● Tutorial will focus on writing very basic C++ in a similar manner to Java
○ But C++ can do SO much more...
C++ Basics● Compiled, statically-typed (like Java)● Nearly backwards-compatible with C● Separates class/function declarations from definitions (unlike Java)
○ Declarations stored in header files (.h, .hpp, .hxx, etc.)○ Definitions stored in source files (.cc, .cpp, .cxx, etc.)
● Source files are compiled into object files, which then pass through the linker to become the final executable
C++ Program Lifecycle
Hello WorldHelloWorld.java (Java)
public class HelloWorld { public static void main(String[] args) { System.out.println( “Hello, World!” ); }}
Compile and run:
javac HelloWorld.java && java HelloWorld
HelloWorld.cpp (C++)
#include <iostream>
int main(int argc, char* argv[]) { std::cout << “Hello, World!\n” ;
return 0;}
Compile and run (uses GCC):
g++ HelloWorld.cpp -o hello && ./hello
C++ Basics - Primitive Types● Compared with Java’s nine primitive types (boolean, byte, char, short,
int, long, float, double, and void), C++ has many primitive types
C++ Basics - Control Flow● Branching constructs (if/else, switch) are identical to Java● Looping constructs (for/while/do-while) are also identical to Java
○ One exception: Java’s “for each” construct (for (Type t : collection) { … } ) is only available in C++11 and onwards
C++ Basics - Functions● Unlike Java, functions in C++ can be free-floating and not tied to a class● Otherwise, quite similar to Java
#include <cstdio>
// Print some values using C’s printf() functionvoid foo(int i, float f, bool b) {
std::printf(“i: %d, f: %f, b: %d\n”, i, f, b); }
C++ Basics - Functions
myfunc.h
// Declare function prototypevoid foo(int a);
myfunc.cpp
#include “myfunc.h”
// Define functionvoid foo(int a) {
// ...}
● Typically, C++ functions (and classes) are split into their declarations (header file) and their definitions (source file)
C++ Basics - Classes● Also fairly similar to Java● Can be defined with either class or struct keywords
○ struct member visibility defaults to public, class to private
● Unlike Java, classes can either be allocated on the stack or on the heap○ We’ll get back to this
C++ Basics - Classes
MyClass.h
// Declare class prototypeclass MyClass {public: MyClass(int n); // Constructor int n; // Fieldprivate: void foo(); // Method};
MyClass.cpp
#include “MyClass.h”
MyClass::MyClass( int n) {// ...
}
void MyClass::foo() {// ...
}
● Like functions, C++ classes are split into their declarations (header file) and their definitions (source file)
Detour - Stack vs Heap
C++ Basics - Classes#include “MyClass.h”
int main() {// Create new class instance on stack (memory// deallocated automatically at end of main())MyClass m1(1);m1.n = 42;
// Create new class instance on heap (memory// must be deallocated manually via delete!)MyClass* m2 = new MyClass(2);if (m2 != NULL) { // Or nullptr in C++11 and onwards
m2->n = 42;}delete m2;
}
C++ Basics - Arrays● Inherits arrays directly from C● Unlike Java arrays, C/C++ arrays can be allocated on either the stack or the
heap● Unlike Java arrays, C/C++ arrays are not objects
○ Basically just pointers to blocks of memory (whether stack or heap allocated)○ Don’t carry their length around with them
● So-called C-style strings are just arrays of char○ WARNING: C-style strings must include room for the null terminator ‘\0’
C++ Basics - Arraysint main() {
// Allocate an uninitialized array of 20 ints on stackint arr1[20];arr1[0] = 1;
// Allocate and initialize an array of 5 ints on stackint arr2[] = {1,2,3,4,5};
// Allocate an array of 20 ints on heap. Must be freed// manually using delete[] (note the brackets for arrays!)int* arr3 = new int[20];delete[] arr3;
}
C++ Basics - Arraysint main() {
// Allocate an uninitialized string of length 19 (plus null// terminator) on stackchar str1[20];
// Allocate and initialize a string of length 5 on stack with// room for the null terminatorchar str2[] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’];
// Allocate and initialize a string from a string literal on stack, // which automatically includes the null terminator for youchar str3[] = “Hello”;
// Get pointer to READ-ONLY literal (stored in static section, can’t// be modified)const char* str4 = “Hello”;
// Allocate a string of length 19 (plus null terminator) on heap. // Must be freed manually using delete[] (note the brackets for arrays!)char* str5 = new char[20];delete[] str5;
}
Arrays and C-Style Strings Suck● Don’t use them
○ Too many ways to do it wrong○ Must manually keep track of length/capacity○ If dynamically allocated, must free memory yourself
● They exist purely for compatibility with C● For C++, we have std::vector<T> and std::string, respectively
std::vector<T>
● Analogous to Java’s ArrayList<T>● See http://www.cplusplus.com/reference/vector/vector/
#include <vector>#include <cstdio>
int main() {std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);
if (v.size() == 3) {// Can also use v.at(2) to access value with bounds checkingstd::printf(“Three things. Last item: %d\n”, v[2]);
}} // v’s memory is automatically freed here
std::string
● Analogous to Java’s String● See http://www.cplusplus.com/reference/string/string/
#include <string>#include <cstdio>
int main() {std::string s1 = “Hello”;std::string s2 = “World”;
if (s1 != s2) {std::string greeting = s1 + “, “ + s2 + “!”;std::printf(“%s\n”, greeting.c_str()); // .c_str() gets the underlying char*
}} // s1’s/s2’s memory is automatically freed here
C++ Tips● If you don’t need polymorphism or nullable objects, don’t use pointers
○ Stack allocation is easier to keep track of (at the cost of performance for large objects)○ If you do need polymorphism, don’t use raw new/delete: smart pointers like
std::unique_ptr<T> and std::shared_ptr<T> manage memory for you (if you’re using C++11)
● Prefer C++ to C when writing C++○ Don’t use raw arrays; use std::vector<T> instead○ Don’t use raw char* strings; use std::string instead
CMake● Cross-platform meta-build system
○ De-facto standard C++ build system for cross-platform projects
● Meta-build system○ Generates other build files that do the actual building○ Can generate Makefiles (Linux), Xcode projects (MacOS), Visual Studio solutions (Windows),
and many more
● Build process is described using CMake’s custom scripting language in a file called CMakeLists.txt
CMakeLists.txt# Set minimum CMake versioncmake_minimum_required(VERSION 3.0)
# Set project nameproject(hello-world)
# Create SOURCES variableset(SOURCES src/main.cpp)
# Create executable named main from SOURCES variableadd_executable(main ${SOURCES})
CMake - Hello World.|___ build|___ CMakeLists.txt|___ src |___ main.cpp
src/main.cpp
#include <cstdio>
int main() {std::printf(“Hello, World!\n”);
return 0;}
What We Didn’t Cover● Polymorphism● References● Special Member Functions/Rule of 3/5/0● Operator Overloading● Const-Correctness● Standard library● Most Standard Template Library (STL) containers and algorithms● Rvalue references and move semantics● Smart pointers● Templates● Advanced CMake
Project Prerequisites - Get the Tools
Sample Project
Set your paths● Remember to make sure you set your NDK path…● ndk.dir=$YourPATH$/Android/sdk/ndk-bundle
Changes in gradle● CMake Compile options…
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
argumetns '-DANDROID_TOOLCHAIN=clang', '-DANDROID_TOOLCHAIN=clang'
cppFlags '...'
cFlags '...'
}
}
Native Functions● Java functions that are used to call functions in your C/C++ code.
● Use the keyword native to distinguish from other functions.
public native ReturnType FuncName(argruments...);
● Declare all the native functions in your one place (MainActivity)
Native functions● Example Declarations
// Return type string. Void parameterspublic native String stringFromJNI();
//Return type void. Int parameters
public native void SetWidth(int width);
//Function to add two numbers
public native int AddNums(int a, int b);
Load the C/C++ library● Let Java know about the function calls in your C/C++
System.loadLibrary("LibName");
LibName - Library built from your C/C++ code
● Make sure to include that code in static block
Static {
System.loadLibrary("LibName");
}
Java JNI interface C/C++● Java provides a way(not exactly an interface) for C/C++ functions to be
callable from JAVA.
● #include<jni.h> ( A Header file)
● Defines two variables JNIEnv* , JavaVM*
● Make sure you include this header file in your C/C++ code
● All declarations, data types, preprocessing definitions are in this header file
JNI data types
JNI reference types
JNI C Code// Java Code
public native ReturnType FuncName(argruments...);
Application ID: Mentioned in gradle file
// C Code#include<jni.h>
……...
…....
JNIEXPORT JNIReturnType JNICALL
Java_ApplicationID_ClassName_FuncName( JNIEnv* env, jobject thiz,
arguments... ) { …… };
JNI C functionsapplicationId 'com.example.hellojni'
className HelloJNI
//Return type string. Void parameterspublic native String stringFromJNI();
// Corresponding C functionJNIEXPORT jstring JNICALL
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz);//Return type void. Int parameters
public native void SetWidth(int width);
//Function to add two numbers
public native int AddNums(int a, int b);
JNI C functionsapplicationId 'com.example.hellojni'
className HelloJNI
//Return type string. Void parameterspublic native String stringFromJNI();
// Corresponding C function
JNIEXPORT jstring JNICALL
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz);
//Return type void. Int parameters
public native void SetWidth(int width);
??????????????????????????????????? ( C/C++ Function?)//Function to add two numbers
public native int AddNums(int a, int b);
??????????????????????????????????? ( C/C++ Function?)
JNI C functionsapplicationId 'com.example.hellojni'
className HelloJNI
//Return type void. Int parameters
public native void SetWidth(int width);
JNIEXPORT void JNICALL
Java_com_example_hellojni_HelloJni_SetWidth( JNIEnv* env, jobject
thiz, jint width);
//Function to add two numbers
public native int AddNums(int a, int b);
JNIEXPORT int JNICALL
Java_com_example_hellojni_HelloJni_AddNums( JNIEnv* env, jobject
thiz, jint a, jint b);
JNI C++ functions
#include<jni.h>
……...
…....
extern "C" {
//JNI Native C functions
JNIEXPORT JNIReturnType JNICALL
Java_ApplicationID_ClassName_FuncName( JNIEnv* env, jobject thiz,
arguments... ) { …… };
}
Calling Java Functions● Java functions can be called from native code ( C/C++)
● #include<jni.h> ( A Header file)
● Defines two variables JNIEnv* , JavaVM*
● Defines several functions to retrieve classes,
methods, and variables
JNI Functions● Some of the widely used functions
jclass FindClass(JNIEnv *env, const char *name);
jmethodID GetMethodID(JNIEnv *env, jclass clazz,const char *name, const
char *sig);
NativeType Call<type>Method(JNIEnv *env, jobject obj,jmethodID methodID,
...)
jfieldID GetFieldID(JNIEnv *env, jclass clazz,const char *name, const char
*sig);
JNI Functions
● Using the JNI functions in C/C++ code○ Create objects of Java classes○ Get Methods of the classes○ Call private, public, and static methods of the classes
Constructor like function● Special native function called when System.loadLibrary call is made
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
● Use this function load and save several environmental variables● Corresponding JNI_OnUnLoad() function exists to free memories
Example OnLoad CodeJNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) !=
JNI_OK) {
return JNI_ERR; // JNI version not supported.
}
}
● Let’s look at a detailed example of callbacks
Questions?
Link to code:
https://github.com/googlesamples/android-ndk
Examples discussed in class
● hello-jni, hello-jniCallback
Advanced examples
● hello-gls , native-activity, native-audio
Points to cover● Settings side
○ Path of the NDK set○ Changes in gradle ○ Add dependencies and path to CMake
● Java Side○ Declaring native functions○ Setting the load library name on start up○ Calling a C function code○ Talk about @keep ( code optimization)
● C/C++ Side○ What is a Java JNI - one slide recap○ Data types ( jClass, jMethod, jObject, jInt, jString…)○ Declaring a JNI callable function in C ( if in C++ use extern)○ Signature of the function○ Parameters passed! ○ Calling a Java Function