In previous tutorial we learned how to use Java Native Access (JNA) library to call native method this native method is availabe in android prebuild shared library (libc). In this tutorial we will create our own C++ shared library and call with JNA library.
Create new android project and change Application name you can also change Company domain and Package name according to requirements, select Include C++ support and click next
Select minimum SDK version and click next
Select Empty Activity and click next
Change Activity Name and Layout Name according to requirements and click Next
Select C++ Standard as Toolchain Default and click Finish to create project
Download jna.aar and
create New Module and select Import JAR/AAR Package and click next
Select jna.aar file from file system and click Finish
jna module added to project, open build.gradle file and add jna module under dependencies
dependencies {
compile project(':jna')
....
....
}
In this section we will create very simple math library in C++ which will perform basic math operations such as add, subtract, multiply and divide. Open the file native-lib.cpp which is in cpp folder
at the time of writing contents of this file are
#include
#include
extern "C"
JNIEXPORT jstring JNICALL
Java_com_faob_t2_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
As we can see for a very simple method which just returns string, a very complex code has been generated with long method name and complex arguments types. Replace this complex code with a very simple code which does exactly the same thing
extern "C" {
char* libName(){
return (char *) "My Math Shared Library";
}
}
As we can see code is very simple and readable and is easy to maintain. Return type of this method is pointer to char which is standard way to declare string in C++. We don't need complex JNI types and long method names this is one of the power JNA gives us. Lets add some more methods which performs basic math operations complete code is
extern "C" {
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int divide(int a, int b) {
return a / b;
}
int multiply(int a, int b) {
return a * b;
}
char* libName(){
return (char *) "My Math Shared Library";
}
}
Since we are using C++ we must wrap all methods in extern "C" block to avoid name mangling. First four methods returns basic math operations results and fifth method libName just returns string which is name of our library. As we can see this code is much simpler, easy to maintain and understand, equivalent JNI code for these methods will be complex and difficult to maintain this is one of many advantages JNA library gives us.
Open MainActivity.java and replace old code with this new code
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.sun.jna.Native;
public class MainActivity extends AppCompatActivity {
//loading native library
static {
Native.register(MainActivity.class, "native-lib");
}
//native methods signatures
public native int add(int a, int b);
public native int subtract(int a, int b);
public native int divide(int a, int b);
public native int multiply(int a, int b);
public native String libName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//calling native methods
System.out.println("add: " + add(4, 2));
System.out.println("subtract: " + subtract(4, 2));
System.out.println("divide: " + divide(4, 2));
System.out.println("multiply: " + multiply(4, 2));
System.out.println(libName());
}
}
static {
Native.register(MainActivity.class, "native-lib");
}
First argument to Native.register() method is the class in which we defined our native methods and 2nd argument is the name of native shared library. Name of shared library is native-lib we can change this default name in CMakeLists.txt file which is available in app folder. Loading should be done in static block which will load shared library during class loading time.
native int add(int a, int b);
native int subtract(int a, int b);
native int divide(int a, int b);
native int multiply(int a, int b);
native String libName();
Mapping C++ functions in Java is very simple first four C++ methods are almost same in Java and fifth C++ method which return pointer to char which is String in java. we have to add native keyword which tells compiler that method is implemented in native code.
System.out.println("add: " + add(4, 2));
System.out.println("subtract: " + subtract(4, 2));
System.out.println("divide: " + divide(4, 2));
System.out.println("multiply: " + multiply(4, 2));
System.out.println(libName());
Calling native methods is same as calling other java methods.
Output can be seen in Logcat window.
With latest Android Studio versions it becomes very easy to add native code in android project and with JNA library it becomes super simple to call native code.
Project is available on github. Clone repo and open project name T2.