How to use extra *.so libraries on Android Studio and NDK
A

1

8

I am trying to generate an Android application to use some extra *.so libraries (specifically 'libinterface.so'). Those libraries are generated externally, and included as a dependency inside a wrapper class called from Java side. The library is stored into 'src/main/jniLibs/armeabi-v7a'. The system includes all the .so file into the generated app.

Previously, I was using Eclipse for this purpose and I was able to use this library, but I have problems to do this with Android Studio.

The generated error is:

/home/******/Libraries/android-sdk-linux/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/../lib/gcc/aarch64-linux-android/4.9/../../../../aarch64-linux-android/bin/ld: cannot find -linterface

As the error is thrown by the linker, it looks related with the library inclusion step. On Eclipse, I was using an 'Android.mk' file to include the new library, but I can't find the way to do this using Gradle.

#Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libinterface-prebuilt
LOCAL_SRC_FILES := prebuilt/libinterface.so
include $(PREBUILT_SHARED_LIBRARY)

I am trying to include the libraries with this gradle definition (Note: I have included the last JNI support and gradle-experimental using this tutorial):

...
android.buildTypes {
    release {
        minifyEnabled = false
        proguardFiles.add(file('proguard-android.txt'))
    }
}

android.ndk {
    moduleName = "custom_wrapper_jni"
    cppFlags.add("-I" + file("src/main/jni").absolutePath)
    cppFlags.add("-I" + file("../Integration/include").absolutePath)  // <- New library header include path 
    cppFlags.add("-L" + file("src/main/jniLibs/armeabi-v7a").absolutePath)  // <- Path where the library is stored
    cppFlags.add("-std=c++11")
    stl = "stlport_static" // Which STL library to use: gnustl or stlport
    ldLibs.add("log")

    ldLibs.add("interface")    //<- Library to be included
}
...

The library is compiled externally using CMake and makefile tools, and it is cross-compiled 'correctly' for Android platform (tested with Eclipse and ADT).

I have implemented the wrapper like this:

// custom_wrapper_jni.h
#ifndef ANDROID_JNI_H
#define ANDROID_JNI_H

#include <jni.h>

extern "C"
{
    JNIEXPORT jint JNICALL
    Java_com_example_goe_android_JniInterface_testFunction(JNIEnv *env,
                                                           jobject instance);
}
#endif

and

// custom_wrapper_jni.cpp
#include <custom_wrapper_jni.h>

#include "Interface.h"   // Header of the included library

Interface* mInterface = Interface::create();  // Generate the library class instance

JNIEXPORT jint JNICALL
Java_com_example_goe_android_JniInterface_testFunction(JNIEnv *env,
                                                    jobject instance)
{
    LOGI("Test function called in wrapper!");
    return mInterface->test();    // Use the instance
}

The header of the library looks like this:

#ifndef INTERFACE_H__
#define INTERFACE_H__

#include <string>

class Interface
{
public:
    static Interface* create();
    virtual ~Interface(){}


    // Testing function
    virtual int test() = 0;

protected:
    Interface();
};
#endif // INTERFACE_H__

Thanks in advance.

UPDATE:

Following this example, I have included some blocks into gradle script:

def lib_path = file("src/main/jniLibs").absolutePath

model {

    repositories {
        libs(PrebuiltLibraries) {
            newlibs {
                headers.srcDir file("../Integration/include").absolutePath
                binaries.withType(SharedLibraryBinary) {
                    sharedLibraryFile = file("${lib_path}/${targetPlatform.getName()}/libinterface.so")
                    println "Included libraries: " + file("${lib_path}/${targetPlatform.getName()}/libinterface.so")
                }
            }
        }
    }

    android {
     ...
    }

    android.sources {
        main {
            jni {
                dependencies {
                    library "newlibs" linkage "shared"
                }
            }
        }
    }
}

but is not working:

Error: org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationFailure: Linker failed while linking libcustom_wrapper_jni.so.
Ample answered 16/3, 2016 at 11:45 Comment(7)
Where do you put libinterface.so?Rugen
The library is stored in 'src/main/jniLibs/armeabi-v7a'. As it could be important info, I have updated the question.Ample
Can other .so libraries be loaded correctly?Rugen
I am testing only with this library. I could test also with other libraries. Any suggestion?Ample
Maybe you can try whether other libraries are ok.Rugen
Following your suggestion, I am trying to load OpenCV libraries.Ample
I can load OpenCV libraries, but when I include my .so, the application crashes. I will update the question with the new info.Ample
T
5

Ok, there could be two different issues.

First, you have to be sure that the library is compiled for the correct architecture. If you are using an armeabi-v7a library, but the compiler is trying to load an armeabi library the compilation will fail.

Second, and following also with the first issue, you have to include the libraries depending the used architecture. Use the 'flavours' configuration in your module build.gradle script.

In example, you can try to do something like this:

android.productFlavors {
    create("arm") {
        ndk.with{
            abiFilters.add("armeabi")

            File curDir = file('./')
            curDir = file(curDir.absolutePath)
            String libsDir = curDir.absolutePath + "/src/main/jniLibs/armeabi/"

            ldLibs.add(libsDir + "libinterface.so")
        }
    }
    create("armv7") {
        ndk.with {
            abiFilters.add("armeabi-v7a")

            File curDir = file('./')
            curDir = file(curDir.absolutePath)
            String libsDir = curDir.absolutePath + "/src/main/jniLibs/armeabi-v7a/"

            ldLibs.add(libsDir + "libinterface.so")
        }
    }
}

Furthermore, I suggest you to use 'jniLibs' to store the libraries, because it is the default path for them, but use different folder for each arch.

You can check other examples like this.

Hope this helps. Greetings.

Trillion answered 17/3, 2016 at 13:43 Comment(1)
How could one compile fat library with this approach?Vedic

© 2022 - 2024 — McMap. All rights reserved.