Search code examples
androidandroid-ndkjava-native-interfaceandroid-gradle-pluginandroid.mk

How to migrate an Android NDK project based on Android.mk files to Gradle when a native shared library depends on a static library?


I have an Android NDK project which builds and runs fine with Android.mk files, it consists of my native code (one C file) built as a shared library, this code has a dependency to a third party static library (.a file).

Right now I am trying to migrate it to Gradle. My current configuration is as follows:

Static library Android.mk that is under the /static_libs folder



    LOCAL_PATH := $(call my-dir)

    include $(CLEAR_VARS)

    LOCAL_MODULE    := opus
    LOCAL_SRC_FILES := lib/libopus.a
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include

    include $(PREBUILT_STATIC_LIBRARY) 

Main module Android.mk file: (under the usual jni/ folder)



    LOCAL_PATH := $(call my-dir)
    $(call import-add-path,$(LOCAL_PATH)/../static_libs)

    include $(CLEAR_VARS)

    LOCAL_MODULE:=opus_jni

    LOCAL_SRC_FILES:= opus_jni.c

    LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -ldl 
    LOCAL_WHOLE_STATIC_LIBRARIES := opus
    include $(BUILD_SHARED_LIBRARY)

    $(call import-module,opus) 

When I run ndk-build on the main module the only output is one file libopus_jni.so that is the only one used to build the final APK.

Now on Gradle, the only thing I want to do for now is create an APK with this same prebuilt shared library and the same code, so I copied my generated library into jniLibs/ folder (I know as of 0.9 Gradle already supports JNI libraries if the files are put here). The project builds and the final APK does contain the .so file inside the libs/ folder. (I verified by unzipping the generated APK)

The problem is that when I try to use any of the native methods I get the following error:



    05-28 14:57:53.393    3370-3370/com.opusgradle.app D/OB-OpusCodec﹕ testPcmToOpus()
    05-28 14:57:53.393    3370-3370/com.opusgradle.app D/dalvikvm﹕ Trying to load lib /data/app-lib/com.opusgradle.app-2/libopus_jni.so 0x42630b18
    05-28 14:57:53.393    3370-3370/com.opusgradle.app D/dalvikvm﹕ Added shared lib /data/app-lib/com.opusgradle.app-2/libopus_jni.so 0x42630b18
    05-28 14:57:53.393    3370-3370/com.opusgradle.app D/OB-OpusCodec﹕ Trying to initialize...
    05-28 14:57:53.393    3370-3370/com.opusgradle.app W/dalvikvm﹕ No implementation found for native Lcom/opusgradle/app/OpusCodec;.initOpusEncoder:(II)V
    05-28 14:57:53.393    3370-3370/com.opusgradle.app D/AndroidRuntime﹕ Shutting down VM
    05-28 14:57:53.393    3370-3370/com.opusgradle.app W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x415e0ba8)
    05-28 14:57:53.393    3370-3370/com.opusgradle.app E/AndroidRuntime﹕ FATAL EXCEPTION: main
        Process: com.opusgradle.app, PID: 3370
        java.lang.UnsatisfiedLinkError: Native method not found: com.opusgradle.app.OpusCodec.initOpusEncoder:(II)V
                at com.opusgradle.app.OpusCodec.initOpusEncoder(Native Method)
                at com.opusgradle.app.OpusCodec.(OpusCodec.java:23)
                at com.opusgradle.app.MainActivity.testPcmToOpus(MainActivity.java:78)
                at com.opusgradle.app.MainActivity.access$000(MainActivity.java:22)
                at com.opusgradle.app.MainActivity$1.onClick(MainActivity.java:64)
                at android.view.View.performClick(View.java:4438)
                at android.view.View$PerformClick.run(View.java:18422)
                at android.os.Handler.handleCallback(Handler.java:733)
                at android.os.Handler.dispatchMessage(Handler.java:95)
                at android.os.Looper.loop(Looper.java:136)
                at android.app.ActivityThread.main(ActivityThread.java:5017)
                at java.lang.reflect.Method.invokeNative(Native Method)
                at java.lang.reflect.Method.invoke(Method.java:515)
                at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
                at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
                at dalvik.system.NativeStart.main(Native Method)

As you can see the .so file is found and loaded but the call to the native function fails. The Java code is the same as in the original project that works fine, basically the invocation to the library looks like this:



    static 
    {
     System.loadLibrary("opus_jni");
    }

It seems that when a static library is required, Gradle needs something else copied into the jniLibs folder, I tried copying the .a file to the jniLibs/ folder too without any success.

Is there any other requirement for pre-built shared librares with a static library dependency to build correctly on Gradle?


Solution

  • Most likely, you changed the package name for the Java class that uses your native library. Usually, the native method names are hardcoded to satisfy JNI automatic binding. That's why you cannot use the prebuilt libopus_jni.so.

    The easiest resolution would be to rename the class com.opusgradle.app back (was it com.opus.app?). You can change this class without renaming the app package in AndroidManifest.xml.

    Alternatively, you can rename the Jni native method names in opus_jni.c, rebuild the library, and copy it to the jniLibs/ folder for gradle.