In this tutorial we will learn how to rename shared library and build multiple shared libraries. Instead of putting all C/C++ code in one single file and create one big shared library what we can do is to create multiple shared libraries. Advantage of this approach is that we can have shared library for each specific task, we can easily manage C/C++ code and we can reuse small individual shared libraries in other projects.
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
If you get NDK not configured error in Messages window then click on File menu and select Project Structure and set Android NDK location.
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')
....
....
}
At the time of writing this post, default name for shared library generated by android studio is native-lib we can change this default name in CMakeLists.txt file located in Project window and under External Build Files.
Most of the contents in this file are comments remove everything and use these lines to make things easier.
cmake_minimum_required(VERSION 3.4.1)
add_library( foo SHARED src/main/cpp/native-lib.cpp )
First line is the version of cmake and in second line we defined our shared library. First argument to add_library is the name of library we renamed it from native-lib to foo, 2nd argument is type of library we want in our case its SHARED and 3rd argument is the file path which contains our C/C++ code.
In this section lets create two shared libraries replace code in CMakeLists.txt file with this code
cmake_minimum_required(VERSION 3.4.1)
add_library( foo SHARED src/main/cpp/foo.cpp )
add_library( bar SHARED src/main/cpp/bar.cpp )
Second line will create foo shared library pointing to foo.cpp file which we will create and 3rd line will create bar shared library pointing to bar.cpp. lets create these two C++ files, delete native-lib.cpp file under src/main/cpp folder. Right click on cpp folder in Project window and click C/C++ Source File, name one file foo and other bar and select type .cpp
Since we are using C++ the functions we want to export in shared library must be wrapped in extern "C" block to avoid name mangling.
foo.cpp file will contain only one method greet which will return greeting message as String, open foo.cpp file and put this code
extern "C" {
char *greet() {
return (char *) "Hello World";
}
}
bar.cpp file will contain one method randomNumber which will generate new random number between 1-100 on every call, open bar.cpp and put this code
#include
#include
extern "C" {
int randomNumber() {
srand((unsigned int) time(NULL));
return rand() % 100 + 1;
}
}
In this method we first initialize the random number generator with seed value which is current time in seconds. For every call to time(NULL) will give new time as seed value an hence we get more randomness. We are passing NULL to time() as arguments since we don't want to store that time value in pointer variable. Last line calculates random number between 1-100 and returns it.
Mapping C++ shared libraries in java is very simple we will create Java class for each shared library and map native methods inside those classes so that we can easily manage code. foo library contains one method greet that returns String and bar library contains one method randomNumber that returns int. We must add native keyword to methods which tells compiler that method is implemented in native code
class FooLib {
public static native String greet();
}
class BarLib {
public static native int randomNumber();
}
Lets see whether multiple shared libraries generated or not, click on Build menu and select Build APK(s) then pop up menu appears in right corner then click on analyze to see whats inside that apk.
As we can see in lib folder shared libraries generated for many different platforms and architectures. If we open one of these platform we can see libfoo.so and libbar.so generated.
static {
Native.register(FooLib.class, "foo");
Native.register(BarLib.class, "bar");
}
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. Names of shared libraries foo and bar defined in Building multiple C++ shared libraries section. Loading should be done in static block which will load shared libraries during class loading time.
System.out.println("Message = " + FooLib.greet());
System.out.println("Ranadom Number = " + BarLib.randomNumber());
Native methods in Java are defined as static so we can call them with class name it can be non static as well. Calling native methods is same as calling other java methods.
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.sun.jna.Native;
public class MainActivity extends AppCompatActivity {
static {
Native.register(FooLib.class, "foo");
Native.register(BarLib.class, "bar");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("Message = " + FooLib.greet());
System.out.println("Ranadom Number = " + BarLib.randomNumber());
}
}
class FooLib {
public static native String greet();
}
class BarLib {
public static native int randomNumber();
}
Output can be seen in Logcat window.
Newer Android Studio versions has excellent support for cmake now it becomes very easy to create and mange multiple shared libraries and with technologies like JNA we can easily call native code.
Complete project available on github. Clone repo and open project name T3.