Building and using multiple android shared libraries

in utopian-io •  7 years ago  (edited)

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

pic1.png

Select minimum SDK version and click next

pic2.png

Select Empty Activity and click next

pic3.png

Change Activity Name and Layout Name according to requirements and click Next

pic4.png

Select C++ Standard as Toolchain Default and click Finish to create project

pic5.png

If you get NDK not configured error in Messages window then click on File menu and select Project Structure and set Android NDK location.

pic6.png

2. Configuring JNA AAR library

Download jna.aar and
create New Module and select Import JAR/AAR Package and click next

pic8.png

Select jna.aar file from file system and click Finish

pic9.png

jna module added to project, open build.gradle file and add jna module under dependencies

dependencies {
    compile project(':jna')
    ....
    ....
}

p9.png

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.

pic10.png

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

pic11.png

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.

pic13.png

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.

pic12.png

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

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

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

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • Seems like you contribute quite often. AMAZING!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

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