Overview
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.
Tutorial covers
- Creating Android Studio project
- Configuring JNA AAR library
- Renaming C++ shared library
- Building multiple C++ shared libraries
- Mapping C++ shared libraries in Java
- Analyzing APK file
- Loading and calling C++ shared libraries in Java
- Output
Requirements
- Android Studio 3.0 or higher
- Android NDK
Difficulty
- Intermediate
Guide
1. Creating Android Studio project
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.
2. Configuring JNA AAR library
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')
....
....
}
3. Renaming C++ shared library
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.
4. Building multiple C++ shared libraries
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 <stdlib.h>
#include <time.h>
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.
5. Mapping C++ shared libraries in Java
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();
}
6. Analyzing APK file
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.
7. Loading and calling C++ shared libraries in Java
Loading shared libraries
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.
Calling native methods
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.
Complete Java code
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();
}
8. Output
Output can be seen in Logcat window.
Conclusion
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.
Code
Complete project available on github. Clone repo and open project name T3.
Posted on Utopian.io - Rewarding Open Source Contributors
Thank you for the contribution. It has been approved.
You can contact us on Discord.
[utopian-moderator]
Hey @portugalcoin, I just gave you a tip for your hard work on moderation. Upvote this comment to support the utopian moderators and increase your future rewards!
Hey @kabooom I am @utopian-io. I have just upvoted you!
Achievements
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x