11

I am creating an Android library (.aar file) and I need to use JNI. (I am very well aware of Google's discouragement of using JNI/NDK if possible, but in this case, it's not possible).

I started with a standalone hello-jni example APP (to first learn JNI), with the following files:

HelloJni.java

public class HelloJni extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
    }

    public native String  stringFromJNI();

    static {
        System.loadLibrary("hello-jni");
    }
}

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI := all

hello-jni.c

#include <string.h>
#include <jni.h>

jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI!");
}

The following builds fine as an app (apk) and I am able to run it on my device, which prints "Hello from JNI!" as expected.

Now, I did the same thing, but instead of an apk, I built a library project to produce an aar. I kept all the files the same except HelloJni.java, which I changed to the following:

HelloJni.java

public class HelloJni {
    public native String stringFromJNI();

    static {
        System.loadLibrary("hello-jni");
    }
}

The aar builds fine, but when I import the aar into a separate app project, and try to run it on my device, it crashes on app start and I get the following error logcat message:

com.test.sample.mysampleapplication E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.test.sample.mysampleapplication, PID: 20047 java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.test.sample.mysampleapplication-1/base.apk"],nativeLibraryDirectories=[/data/app/com.test.sample.mysampleapplication-1/lib/arm, /vendor/lib, /system/lib]]] couldn't find "libhello-jni.so" at java.lang.Runtime.loadLibrary(Runtime.java:366) at java.lang.System.loadLibrary(System.java:988) ...

What in the world is this "libhello-jni.so" file? And why do I need it? I was able to run this perfectly fine as an apk. But can anyone explain why it doesn't work when I make it into an aar, and import it to an app project to use it? Am I missing some additional steps needed to make it into a library (and use it)? Thanks!

EDIT:

This is how I imported my aar to my app. 1. Click "File" -> "New" -> "New Module". 2. Select "Import .JAR or .AAR Package" as module type. 3. Set my aar file as new module 4. And then open "File" -> "Project Structure" 5. In the "Dependencies" tab, add "Module dependency" and select my aar file If that's not a good way to import aar to my app, then also please let me know. Thank you!

baekacaek
  • 1,527
  • 3
  • 22
  • 45
  • "when I import the aar into a separate app project" -- you may wish to provide much more specifics about what you did here. "What in the world is this "libhello-jni.so" file?" -- that is a Linux `.so` file, the result of compiling C/C++ code to a shared library. "And why do I need it?" -- you wrote C/C++ and are trying to execute it. "I was able to run this perfectly fine as an apk" -- your APK file contains that `.so`. Your AAR should too, which is why you may wish to explain, in detail, how you created the AAR and how you are trying to consume it. – CommonsWare Jul 31 '15 at 19:25
  • Thanks! I added instructions on how I added the aar file as a library in my app. – baekacaek Jul 31 '15 at 21:13
  • An AAR is a ZIP-style archive. Look inside yours and see if you have the `.so` files. If the answer is "no", then the problem is in how the AAR was created, I guess. If the answer is "yes", you may need to switch to using an artifact repository instead of that import option, which I have never tried. I have successfully published AARs with NDK-compiled libraries (e.g., [CWAC-AndDown](https://github.com/commonsguy/cwac-anddown)), so I know it's possible. – CommonsWare Jul 31 '15 at 21:16
  • Thanks again for your quick reply. I built the aar file by typing "gradle assembleDebug" in terminal at the aar project's directory. How do I look inside an aar file? (I'm on a Mac) – baekacaek Jul 31 '15 at 21:27
  • "How do I look inside an aar file?" -- the same way that you would look inside any ZIP archive. I would presume that OS X has **`unzip`** or equivalent tools. – CommonsWare Jul 31 '15 at 21:29
  • Thanks. I unzipped it and inside the /jni/arm64-v8a (there's also mips, x84, etc) directory, there's a libsample-aar.so file. "sample-aar" is my aar project directory. I'm guessing this either needs to be named "libhello-jni.so" or a "libhello-jni.so" file needs to be generated somehow? – baekacaek Jul 31 '15 at 21:40
  • Yeah, something is out of sync there. Android Studio does not use those makefiles, but instead `ndk` closures in the `build.gradle` file. You might wish to take a peek at [the CWAC-AndDown project](https://github.com/commonsguy/cwac-anddown) that I linked to earlier. The `anddown` library module in that project has the JNI stuff and `build.gradle` bits set up and working. – CommonsWare Jul 31 '15 at 21:44
  • I changed my LOCAL_MODULE and LOCAL_SRC_FILES values in Android.mk from "hello-jni" to "sample-aar" and it worked. My app no longer complains about missing "libhello-jni.so" file because it's now looking for "libsample-aar.so" file. Is it required that my C/C++ file be the same exact name as my aar project directory? It seems odd and I suspect something is wrong here, even though it works for now. – baekacaek Jul 31 '15 at 22:08
  • "Is it required that my C/C++ file be the same exact name as my aar project directory?" -- AFAIK, no, but if you have not configured Android Studio's NDK build process, it is probably substituting some default values. – CommonsWare Jul 31 '15 at 22:09
  • Finally found my problem! Turns out I have to add something (see my answer below) in my aar module's build.gradle file. Without this, then it will default to my project's parent directory, which was happening in my case. Thanks for helping! – baekacaek Jul 31 '15 at 23:18

2 Answers2

6

Turns out I had to add some NDK config in my build.gradle file inside my AAR module directory.

build.gradle:

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"

        // This determines the .so filename
        ndk {
            moduleName "hello-jni"
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

Not having that added in build.gradle will produce an .so file that's defaulted to your project's name, in my case "libsample-aar.so". With that config above, the generated .so file will then be "libhello-jni.so".

baekacaek
  • 1,527
  • 3
  • 22
  • 45
  • 1
    This does not explained how you referenced .so files from within your AAR. This step also can be done in your android.mk LOCAL_MODULE tag. – StarWind0 Apr 24 '20 at 06:02
0

You must build the native code with the command ndk-build from Android NDK.

This proccess will generate the linhello-jni.so that will be included in your build.

Marcos Vasconcelos
  • 18,136
  • 30
  • 106
  • 167
  • Can you show me how to run the ndk-build command? This is what I get: – baekacaek Jul 31 '15 at 22:12
  • $ ndk-build Android NDK: Could not find application project directory ! Android NDK: Please define the NDK_PROJECT_PATH variable to point to it. – baekacaek Jul 31 '15 at 22:12