0

I am using JNI in an Android Studio project I am working on. Currently, I have a C++ library that looks similar to this.

#include <jni.h>
...
extern "C" {
    JNIEXPORT jobject JNICALL Java_com_cerbyarms_cerbyarms_esra_camera_CameraActivity_FindFeatures(JNIEnv* env, jobject, jlong maskMat)
    {
        ...
        jclass rectClass = env->FindClass("org/opencv/core/Rect");
        jmethodID rectID = env->GetMethodID(rectClass, "<init>", "(IIII)V");
        return env->NewObject(rectClass, rectID, x, y, width, height);
    }
}

This works. However, it is inefficient. Every time this is run, rectClass has to refind the class and other variables that remain constant in the program have to be recalculated and redefined every time function FindFeatures is called.

I came across this answer on Stack Overflow (It is not related to this question apart from the fact that it shows an example of what I am trying to do), that shows a different layout for a native file when using JNI.

It looked like this

static jclass java_util_ArrayList;
static jmethodID java_util_ArrayList_;
jmethodID java_util_ArrayList_size;
jmethodID java_util_ArrayList_get;
jmethodID java_util_ArrayList_add;
static thread_local JNIEnv* env;

void init() {
    java_util_ArrayList      = static_cast<jclass>(env->NewGlobalRef(env->FindClass("java/util/ArrayList")));
    java_util_ArrayList_     = env->GetMethodID(java_util_ArrayList, "<init>", "(I)V");
    java_util_ArrayList_size = env->GetMethodID (java_util_ArrayList, "size", "()I");
    java_util_ArrayList_get  = env->GetMethodID(java_util_ArrayList, "get", "(I)Ljava/lang/Object;");
    java_util_ArrayList_add  = env->GetMethodID(java_util_ArrayList, "add", "(Ljava/lang/Object;)Z");
}

std::vector<std::string> java2cpp(jobject arrayList) {
    jint len = env->CallIntMethod(arrayList, java_util_ArrayList_size);
    std::vector<std::string> result;
    result.reserve(len);
    for (jint i = 0; i < len; i++) {
        jstring element = static_cast<jstring>(env->CallObjectMethod(arrayList, java_util_ArrayList_get, i));
        const char* pchars = env->GetStringUTFChars(element, nullptr);
        result.emplace_back(pchars);
        env->ReleaseStringUTFChars(element, pchars);
        env->DeleteLocalRef(element);
    }
}

This shows a native file that has expensive and constant variables that appear to only be declared and calculated once.

How can I achieve a similar thing using only the Android Studio IDE? I don't mind having to set up external tools in the Android Studio IDE settings, but I don't want to have keep switching between Android Studio and something like CMD every time I compile my code.

Ideally, this could all be handled correctly when Make Project is hit. Is this possible in Android Studio 3?

3
  • "How can I achieve a similar thing using only the Android Studio IDE?" I don't think I understand the question. Why can't you just create a new .cpp file from within Android Studio and write any C++ code you want in it? Commented Mar 24, 2018 at 16:04
  • @Michael I am new to JNI so sorry if it is obvious, but how would I then access that code in a Java file if I wrote any C++ code I wanted in a .cpp file. So from the example in the question, how would I access java2cpp in a Java file? Commented Mar 24, 2018 at 16:18
  • By implementing and exporting a suitable function, just like in the first piece of code you showed. Of course, your Java code has no idea what a std:: vector is, so you'd have to return a jobject or jobjectArray representing e.g. a String[] or List<String>. Commented Mar 24, 2018 at 19:21

1 Answer 1

1

You are 100% right, some JNI values beg to be cached and reused. Class references and method IDs are good examples. Please remember that FindClass() returns a local reference, so you need NewGlobalRef() for each class you keep in cache.

Android Studio does not help us with this setup, and I am not aware of reliable tools that can do such refactoring for us. You can learn good practices from open source code, e.g. from WebRTC JNI wrapper or from Spotify JNI helpers.

Android Studio can only keep track of the native methods, not of the cached objects, conversions, etc.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, NewGlobalRef was exactly what I needed. Also those links will be useful as I have never done JNI before so learning good practices for it will be great

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.