Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.
like image 417
goe Avatar asked Mar 16 '16 11:03

goe


People also ask

How do I open .so files on Android?

Actually inside your JNI folder, android NDK which convert your native code like c or c++ into binary compiled code that is called "filename.so". You cannot read the binary code . so it wil create lib folder inside your libs/armeabi/ filename.so file. Show activity on this post.

Is NDK necessary for Android studio?

You do not need this component if you only plan to use ndk-build. LLDB: the debugger Android Studio uses to debug native code.

What is difference between Android NDK and sdk?

Android provides Native Development Kit (NDK) to support native development in C/C++, besides the Android Software Development Kit (Android SDK) which supports Java. [TODO] more. NDK is a complex and advanced topics.


1 Answers

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.

like image 50
vgonisanz Avatar answered Sep 21 '22 10:09

vgonisanz